<?php

namespace App\Controller;

use DateTime;
use Exception;
use Parsedown;
use App\Entity\Reference;
use App\Entity\Template;
use App\Entity\User;
use App\Entity\Bible;
use App\Entity\Speaker;
use App\Entity\Series;
use App\Entity\Note;
use App\Entity\SharedNote;
use App\Utils\Utils;
use Doctrine\DBAL\Exception\ReadOnlyException;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\Mailer\MailerInterface;
use Symfony\Component\Mime\Address;
use Symfony\Component\Routing\Attribute\Route;

class AjaxController extends AbstractController
{
    /**
     * A method to retrieve a template
     *
     * @param Request $req description
     * @param EntityManagerInterface $emi description
     *
     * @return Response
     */
    #[Route('/retrieve-template', name: 'app_retrieve_template')]
    public function retrieveTemplate(Request $req, EntityManagerInterface $emi): Response
    {
        $ret = new Response();
        $template_id = json_decode($req->getContent())->template;
        $template = $emi->getRepository(Template::class)->find($template_id);
        $ret->setContent($template->getContent());

        return $ret;
    }

    #[Route('/save-template', name: 'app_save_template')]
    public function saveTemplate(Request $req, EntityManagerInterface $emi): Response
    {
        $ret = new Response();
        $req = json_decode($req->getContent());
        $template_value = $req->template_value;
        $template_id = $req->template_id;
        $template_name = $req->template_name;

        $user = $this->getUser();

        if ($template_id) {
            $template = $emi->getRepository(Template::class)->find($template_id);
            $template->setName($template_name);
            $template->setContent($template_value);
            $template->setUser($user);
            $emi->persist($template);
            $emi->flush();
            $ret->setContent('Updated');
        } else {
            $template = new Template();
            $template->setName($template_name);
            $template->setContent($template_value);
            $template->setUser($user);
            $emi->persist($template);
            $emi->flush();
            $ret->setContent('Added');
        }

        return $ret;
    }

    /**
     * Retrieve a speaker from the database based on the given request and entity manager.
     *
     * @param Request $req The request object
     * @param EntityManagerInterface $emi The entity manager
     * @return Response
     */
    #[Route('/retrieve-speaker', name: 'app_retrieve_speaker')]
    public function retrieveSpeaker(Request $req, EntityManagerInterface $emi): Response
    {
        $ret = new Response();
        $speaker_id = json_decode($req->getContent())->speaker;
        $speaker = $emi->getRepository(Speaker::class)->find($speaker_id);
        $ret->setContent($speaker->getProfile());

        return $ret;
    }

    #[Route('/save-speaker', name: 'app_save_speaker')]
    public function saveSpeaker(Request $req, EntityManagerInterface $emi): Response
    {
        $ret = new Response();
        $data = json_decode($req->getContent());

        $user = $this->getUser();

        $speaker = new Speaker();
        $speaker->setName($data->speakerName)
                ->setUser($user);

        $emi->persist($speaker);
        $emi->flush();
        $ret->setContent(json_encode([
            'id' => $speaker->getId(),
            'msg' => $speaker->getName().' added'
        ]));

        return $ret;
    }

    /**
     * Method to retrieve a series
     *
     * @param Request $req description
     * @param EntityManagerInterface $emi description
     *
     * @return Response
     */
    #[Route('/retrieve-series', name: 'app_retrieve_series')]
    public function retrieveSeries(Request $req, EntityManagerInterface $emi): Response
    {
        $ret = new Response();
        $series_id = json_decode($req->getContent())->series;
        $series = $emi->getRepository(Series::class)->find($series_id);
        $ret->setContent($series->getName());

        return $ret;
    }

    #[Route('/save-series', name: 'app_save_series')]
    public function saveSeries(Request $req, EntityManagerInterface $emi): Response
    {
        $ret = new Response();
        $data = json_decode($req->getContent());

        $user = $this->getUser();

        $series = new Series();
        $series->setName($data->seriesName)
               ->setUser($user);

        $emi->persist($series);
        $emi->flush();

        $ret->setContent(json_encode([
            'id' => $series->getId(),
            'msg' => $series->getName().' added'
        ]));

        return $ret;
    }

    #[Route('/retrieve-reference', name: 'app_retrive_reference')]
    public function retrieveReference(Request $req, EntityManagerInterface $emi): Response
    {
        $res = new Response();
        $data = json_decode($req->getContent());
        $search = $data->reference;

        $ref = new Reference();
        $ref->setType($data->type);
        if (is_numeric($data->book)) {
            $ref->setNdx($data->book);
        } elseif ($data->type == 'ld' || $data->type == 'hc') {
            $type = substr($data->book, 0, 2);
            $ref->setType($type);
            $ref->setLabel($data->book);
        }

        ReferenceController::$emi = $emi;
        $user = $this->getUser();

        $ret = match(strtolower($data->type)) {
            'bible' => ReferenceController::retrieveBible("{$data->book} {$search}"),
            'hc' => ReferenceController::retrieveHC($ref),
            'bc' => ReferenceController::retrieveBC($ref),
            'cd' => ReferenceController::retrieveCD($ref),
            'wcf' => ReferenceController::retrieveWCF($ref),
            'wsc' => ReferenceController::retrieveWSC($ref),
            'wlc' => ReferenceController::retrieveWLC($ref),
            'lbc' => ReferenceController::retrieveLBC($ref),
            'creed' => ReferenceController::retrieveCreed($data->book),
            '39a' => ReferenceController::retrieve39a($ref),
            '1hc' => ReferenceController::retrieve1HC($ref),
            '2hc' => ReferenceController::retrieve2HC($ref),
            'sd' => ReferenceController::retrieveSD($ref),
            'agc' => ReferenceController::retrieveAGC($ref),
            'note' => ReferenceController::retrieveNote($user)
        };

        $ref = new Reference();

        if (is_a($ret, Reference::class)) {
            $ref = $ret;
        }

        if ($data->type == 'hc' && is_array($ret)) {
            $ref->setLabel(
                substr($ret[0]->getLabel(), 0, strpos($ret[0]->getLabel(), '-'))
            );

            $content = null;
            foreach ($ret as $row) {
                $content .= $row->getContent().PHP_EOL.PHP_EOL;
            }
            $ref->setContent($content);
        }

        $res->setContent(json_encode([
            'text' => $ref->getContent(),
            'title' => $ref->getLabel(),
        ]));

        return $res;
    }

    #[Route('/get-reference', name: 'app_get_reference')]
    public function getReference(Request $req, EntityManagerInterface $emi): Response
    {
        $res = new Response();
        $data = json_decode($req->getContent());
        $ref = $emi->getRepository(Reference::class)->find($data->id);

        if (!is_a($ref, Reference::class)) {
            $ref = new Reference();
        }
        $res->setContent(json_encode(['text' => $ref->getContent()]));

        return $res;
    }

    #[Route('/save-reference', name: 'app_save_reference')]
    public function saveReference(Request $req, EntityManagerInterface $emi): Response
    {
        $res = new Response();
        $data = json_decode($req->getContent());
        $ref = $emi->getRepository(Reference::class)->find($data->refId);

        if ($ref) {
            $ref->setContent($data->text);
            $emi->persist($ref);
            $emi->flush();
            $res = $this->json(['msg' => 'Reference updated.']);
        }

        return $res;
    }

    #[Route('/search', name: 'app_search_note')]
    public function searchNote(Request $req, EntityManagerInterface $emi): Response
    {
        /** @var User $user */
        $user = $this->getUser();
        $res = new Response();
        $data = json_decode($req->getContent());
        $note = $emi->getRepository(Note::class)->findNote($data->query, $user);
        $res->setContent(json_encode($note));

        return $res;
    }

    #[Route('/get-notes', name: 'app_get_notes')]
    public function getNotes(EntityManagerInterface $emi): Response
    {
        /** @var User $user */
        $user = $this->getUser();
        $notes = $emi->getRepository(Note::class)->reverseNoteSort($user);
        $res = new Response();
        $res->setContent(json_encode(['data'=> $notes]));

        return $res;
    }

    #[Route('/get-note', name: 'app_get_note')]
    public function getNote(Request $req, EntityManagerInterface $emi): Response
    {
        $res = new Response();
        $data = json_decode($req->getContent());
        $note = $emi->getRepository(Note::class)->find($data->id);
        $res->setContent(json_encode($note));

        return $res;
    }

    #[Route('/save-note', name: 'app_save_note', methods: ['POST'])]
    public function saveNote(Request $req, EntityManagerInterface $emi): Response
    {
        $data = json_decode($req->getContent());
        $newNote = false;
        if (isset($data->id) && $data->id) {
            /** @var Note|array $note */
            $dbnote = $emi->getRepository(Note::class)->findBy(['id' => $data->id, 'user' => $this->getUser()]);

            if (is_array($dbnote) && count($dbnote) > 0) {
                /** @var Note $note */
                $note = $dbnote[0];
            } else {
                $note = new Note();
                $newNote = true;
                $note->setUser($this->getUser());
            }
        } else {
            $note = new Note();
            $newNote = true;
            $note->setUser($this->getUser());
        }

        $refs = json_decode(json_encode($data->refs), true);

        $series = $emi->getRepository(Series::class)->find($data->series);
        $speaker = $emi->getRepository(Speaker::class)->find($data->speaker);

        /** @var Note $note */
        $note->setTitle($data->title)
             ->setDate(new DateTime($data->date))
             ->setSeries($series)
             ->setSpeaker($speaker)
             ->setText($data->note)
             ->setPassage($data->passage)
             ->setRecording($data->recording)
             ->setRefs($refs);

        $emi->persist($note);
        $emi->flush();

        $res = new Response();
        $res->setContent(json_encode([
            'msg' => 'saved',
            'id' => $note->getId(),
            'new' => $newNote,
        ]));

        return $res;
    }

    #[Route('/delete-note', name: 'app_discard_note', methods: ['POST'])]
    public function discardNote(Request $req, EntityManagerInterface $emi): Response
    {
        $data = json_decode($req->getContent());
        $note = $emi->getRepository(Note::class)->find($data->id);
        $emi->remove($note);
        $emi->flush();
        $res = new Response();
        $res->setContent(json_encode([
            'msg' => 'deleted'
        ]));

        return $res;
    }

    #[Route('/share-note', name: 'app_share_note', methods: ['POST'])]
    public function shareNote(Request $req, EntityManagerInterface $emi, MailerInterface $mailer): Response
    {
        $data = json_decode($req->getContent());
        $note = $emi->getRepository(Note::class)->find($data->id);
        /** @var User $user */
        $user = $this->getUser();
        $shared = $emi->getRepository(User::class)->findBy(['email' => $data->email]);
        $email = $data->email;
        $isRegistered = false;

        if (!$note) {
            $res = new Response();
            $res->setContent(json_encode([
                'msg' => 'Note Not Found'
            ]));
        }

        if (is_array($shared) && count($shared) > 0) {
            $ns = new SharedNote();
            $ns->setNote($note)
               ->setOwnerId($user->getId())
               ->setSharedUserId($shared[0]->getId());
            $emi->persist($ns);
            $emi->flush();

            $email = $shared[0]->getEmail();
            $isRegistered = true;
        }

        $Parsedown = new Parsedown();

        $util = new Utils();
        $util->sendEmail(
            $mailer,
            new Address('ryan@rkprather.com', 'Sermon Notes'),
            new Address($email),
            'Note Shared',
            $this->renderView('emails/note-shared.html.twig', [
                'note' => $note,
                'owner' => $user,
                'isRegistered' => $isRegistered,
                'formattedText' => $Parsedown->text($note->getText()),
                'domain' => $_ENV['DOMAIN'],
            ])
        );

        $res = new Response();
        $res->setContent(json_encode([
            'msg' => 'shared'
        ]));

        return $res;
    }

    #[Route('/get-passage/{passage}', name: 'app_get_passage')]
    public function getPassage($passage, EntityManagerInterface $emi): Response
    {
        $passage = str_replace('+', ' ', $passage);
        $book = Bible::findBook($passage);
        $chapter = Bible::findChapter($passage);

        $bible = new Bible();
        $bible->setBook($book);
        $bible->setChapter($chapter);
        $verse = Bible::findVerse($passage);
        $verseArr = [];

        if (is_bool($verse) && $verse === false) {
            $verseArr[0] = 1;
            $verseArr[1] = 176;
        } elseif (is_int($verse)) {
            $verseArr[0] = $verse;
            $verseArr[1] = $verse;
        } else {
            $verseArr = $verse;
        }

        $ret = $emi->getRepository(Bible::class)->findRange($bible, $verseArr);

        if (is_array($ret)) {
            $text = null;
            foreach ($ret as $b) {
                $text .= "{$b->getVerse()}. {$b->getContent()}".PHP_EOL;
            }
            $bible->setContent($text);
        } elseif (is_a($ret, Bible::class)) {
            $bible->setContent($ret->getContent());
        }

        $res = new Response();
        $res->setContent($bible->getContent());

        return $res;
    }

    #[Route('/save-profile', name: 'app_save_profile', methods: ['POST'])]
    public function saveProfile(Request $req, EntityManagerInterface $emi): Response
    {
        $data = json_decode($req->getContent());
        /** @var App\Entity\User $user */
        $user = $this->getUser();

        if (!$user) {
            return new JsonResponse(['msg' => 'No User']);
        }

        if ($data->passChange) {
            if(!$data->password) {
                return new JsonResponse(['msg' => 'Blank password']);
            }

            // @todo check that password matches current password
            if ($data->password != $user->getPassword()) {
                return new JsonResponse(['msg' => 'Invalid password']);
            }

            if ($data->newPassword != $data->confPassword) {
                return new JsonResponse(['msg' => 'Passwords don\'t match']);
            }
        }

        $user->setName($data->name);
        $user->setEmail($data->email);
        $user->setHomeChurchRSS($data->homeChurch);

        $emi->persist($user);

        try {
            $emi->flush();
        } catch (Exception $e) {
            return new JsonResponse();
        }

        return new JsonResponse(['msg' => 'Updated']);
    }

    #[Route('/save-settings', name: 'app_save_settings', methods: ['POST'])]
    public function saveSettings(Request $req, EntityManagerInterface $emi): Response
    {
        $data = json_decode($req->getContent());
        /** @var User $user */
        $user = $this->getUser();

        if (!$user) {
            return new Response(json_encode([
                'msg' => 'No User'
            ]));
        }

        if (!$data->saveInterval) {
            $data->saveInterval = 15;
        }

        if (!$data->saveReferences) {
            $data->saveReferences = true;
        }

        if (!$data->noteTextSize) {
            $data->noteTextSize = 12;
        }

        if (!$data->trackSaveSize) {
            $data->trackSaveSize = false;
        }

        try {
            $meta = $user->getMetaData();
            $meta['saveInterval'] = $data->saveInterval;
            $meta['saveReferences'] = $data->saveReferences;
            $meta['noteTextSize'] = $data->noteTextSize;
            $meta['trackSaveSize'] = $data->trackSaveSize;
            $meta['saveTimeout'] = $data->saveTimeout;
            $meta['save-failure-count'] = $data->saveFailureCount;
            $user->setMetaData($meta);
            $emi->persist($user);
            $emi->flush();
        } catch (ReadOnlyException $e) {
            return new Response('Read Only '.$_ENV['DATABASE_URL'], 403);
        }

        $res = new Response();
        $res->setContent(json_encode([
            'msg' => 'Settings Saved'
        ]));

        return $res;
    }
}
