<?php

namespace App\Controllers;

use App\Services\DashboardService;
use App\Services\RecurrenceService;
use App\Services\TaskService;
use CodeIgniter\Exceptions\PageNotFoundException;

class TaskController extends BaseController
{
    public function search()
    {
        $userId = $this->userId();
        if ($userId === null) {
            return redirect()->to('/sign-in');
        }

        $dash     = new DashboardService();
        $tasks    = new TaskService();
        $query    = $dash->normalizeSearchQuery((string) $this->request->getGet('q'));
        $resolved = $dash->resolve($userId, (array) $this->request->getGet(), session()->get(DashboardService::SESSION_KEY));
        $filters  = $resolved['filters'];
        $hrefFilters = $query === '' ? $filters : array_merge($filters, ['q' => $query]);
        $dash->rememberReturn('tasks/search', $hrefFilters);
        $categories = $tasks->categoriesForUser($userId);
        $rows       = $query === '' ? [] : $dash->searchOccurrences($userId, $filters, $query);
        $emptyMessage = $query === ''
            ? DashboardService::MSG_SEARCH_EMPTY
            : $dash->searchEmptyMessage($query);

        return view('tasks/search', [
            'title'          => $query === '' ? 'Search' : $query,
            'activeNav'      => 'home',
            'query'          => $query,
            'dash'           => $dash,
            'filters'        => $filters,
            'filterError'    => $resolved['error'],
            'categories'     => $categories,
            'rows'           => $rows,
            'emptyMessage'   => $emptyMessage,
            'filterBasePath' => 'tasks/search',
            'dateLabel'      => $dash->dateLabel($filters),
            'statusLabel'    => $dash->statusLabel($filters),
            'categoryLabel'  => $dash->categoryLabel($filters, $categories),
        ]);
    }

    public function byPriority(int $priority)
    {
        $userId = $this->userId();
        if ($userId === null) {
            return redirect()->to('/sign-in');
        }

        $dash = new DashboardService();
        if ($dash->sanitizePriority($priority) === null) {
            throw PageNotFoundException::forPageNotFound();
        }

        $tasks    = new TaskService();
        $resolved = $dash->resolve($userId, (array) $this->request->getGet(), session()->get(DashboardService::SESSION_KEY));
        $filters  = $resolved['filters'];
        $dash->rememberReturn('tasks/priority/' . $priority, $filters);
        $categories = $tasks->categoriesForUser($userId);
        $tieasy     = config('Tieasy');

        return view('tasks/priority', [
            'title'          => $tieasy->priorities[$priority],
            'activeNav'      => 'home',
            'priority'       => $priority,
            'dash'           => $dash,
            'filters'        => $filters,
            'filterError'    => $resolved['error'],
            'categories'     => $categories,
            'rows'           => $dash->occurrences($userId, $filters, $priority),
            'emptyMessage'   => $dash->emptyMessage($filters),
            'filterBasePath' => 'tasks/priority/' . $priority,
            'dateLabel'      => $dash->dateLabel($filters),
            'statusLabel'    => $dash->statusLabel($filters),
            'categoryLabel'  => $dash->categoryLabel($filters, $categories),
        ]);
    }

    public function create()
    {
        $userId = $this->userId();
        if ($userId === null) {
            return redirect()->to('/sign-in');
        }

        $tasks = new TaskService();

        return view('tasks/create', $this->formPayload($tasks, $userId, 'create', [
            'title'                 => '',
            'priority'              => (string) ($tasks->sanitizePriority((string) $this->request->getGet('priority')) ?? ''),
            'category_id'           => '',
            'due_date'              => '',
            'due_time'              => '',
            'is_recurring'          => '0',
            'recurrence_type'       => 'daily',
            'interval_value'        => '1',
            'weekly_days'           => [],
            'monthly_type'          => 'day_of_month',
            'monthly_day'           => '',
            'monthly_week_position' => 'first',
            'monthly_weekday'       => '1',
            'recurrence_end_date'   => '',
        ]));
    }

    public function store()
    {
        $userId = $this->userId();
        if ($userId === null) {
            return redirect()->to('/sign-in');
        }

        $tasks  = new TaskService();
        $raw    = $tasks->collect((array) $this->request->getPost());
        $result = $tasks->create($userId, $raw);
        if (! ($result['ok'] ?? false)) {
            return view('tasks/create', $this->formPayload($tasks, $userId, 'create', $this->postedValues($raw), $result['errors'] ?? []));
        }

        return redirect()->to($this->listReturn())->with('success', $result['message']);
    }

    public function edit(int $id)
    {
        $userId = $this->userId();
        if ($userId === null) {
            return redirect()->to('/sign-in');
        }

        $tasks = new TaskService();
        $task  = $tasks->findOwned($userId, $id);
        if ($task === null) {
            return redirect()->to($this->listReturn())->with('error', TaskService::MSG_NOT_FOUND);
        }

        return view('tasks/create', $this->formPayload($tasks, $userId, 'edit', $tasks->formValues($task), [], $id));
    }

    public function update(int $id)
    {
        $userId = $this->userId();
        if ($userId === null) {
            return redirect()->to('/sign-in');
        }

        $tasks = new TaskService();
        $raw   = $tasks->collect((array) $this->request->getPost());
        $result = $tasks->update($userId, $id, $raw);
        if (! ($result['ok'] ?? false)) {
            if (($result['message'] ?? '') === TaskService::MSG_NOT_FOUND) {
                return redirect()->to($this->listReturn())->with('error', TaskService::MSG_NOT_FOUND);
            }

            return view('tasks/create', $this->formPayload(
                $tasks,
                $userId,
                'edit',
                $this->postedValues($raw),
                $result['errors'] ?? [],
                $id,
            ));
        }

        return redirect()->to($this->listReturn())->with('success', $result['message']);
    }

    public function deleteTask(int $id)
    {
        $userId = $this->userId();
        if ($userId === null) {
            return redirect()->to('/sign-in');
        }

        return $this->occurrenceJson((new TaskService())->deleteOwned($userId, $id));
    }

    public function complete(int $id)
    {
        $userId = $this->userId();
        if ($userId === null) {
            return redirect()->to('/sign-in');
        }

        return $this->occurrenceJson((new RecurrenceService())->completeOccurrence($userId, $id));
    }

    public function deleteOccurrence(int $id)
    {
        $userId = $this->userId();
        if ($userId === null) {
            return redirect()->to('/sign-in');
        }

        return $this->occurrenceJson((new RecurrenceService())->deleteOccurrence($userId, $id));
    }

    public function deleteThisAndFuture(int $id)
    {
        $userId = $this->userId();
        if ($userId === null) {
            return redirect()->to('/sign-in');
        }

        return $this->occurrenceJson((new RecurrenceService())->deleteThisAndFuture($userId, $id));
    }

    private function userId(): ?int
    {
        $id = (int) session()->get('user_id');

        return $id > 0 ? $id : null;
    }

    /**
     * @param array<string, mixed> $values
     * @param array<string, string> $errors
     *
     * @return array<string, mixed>
     */
    private function formPayload(TaskService $tasks, int $userId, string $mode, array $values, array $errors = [], ?int $taskId = null): array
    {
        $categories = $tasks->categoriesForUser($userId);
        $backHref   = (new DashboardService())->safeReturn(session()->get(DashboardService::RETURN_KEY));

        return [
            'title'         => $mode === 'edit' ? 'Edit Task' : 'Add Task',
            'activeNav'     => 'home',
            'mode'          => $mode,
            'taskId'        => $taskId,
            'categories'    => $categories,
            'values'        => $values,
            'errors'        => $errors,
            'hasCategories' => $categories !== [],
            'backHref'      => $backHref,
        ];
    }

    private function listReturn(): string
    {
        return (new DashboardService())->safeReturn(session()->get(DashboardService::RETURN_KEY));
    }

    /**
     * @param array<string, mixed> $raw
     *
     * @return array<string, mixed>
     */
    private function postedValues(array $raw): array
    {
        return [
            'title'                 => (string) ($raw['title'] ?? ''),
            'priority'              => (string) ($raw['priority'] ?? ''),
            'category_id'           => (string) ($raw['category_id'] ?? ''),
            'due_date'              => (string) ($raw['due_date'] ?? ''),
            'due_time'              => (string) ($raw['due_time'] ?? ''),
            'is_recurring'          => ! empty($raw['is_recurring']) ? '1' : '0',
            'recurrence_type'       => (string) ($raw['recurrence_type'] ?? 'daily'),
            'interval_value'        => (string) ($raw['interval_value'] ?? '1'),
            'weekly_days'           => is_array($raw['weekly_days'] ?? null)
                ? array_map('strval', $raw['weekly_days'])
                : [],
            'monthly_type'          => (string) ($raw['monthly_type'] ?? 'day_of_month'),
            'monthly_day'           => (string) ($raw['monthly_day'] ?? ''),
            'monthly_week_position' => (string) ($raw['monthly_week_position'] ?? 'first'),
            'monthly_weekday'       => (string) ($raw['monthly_weekday'] ?? '1'),
            'recurrence_end_date'   => (string) ($raw['recurrence_end_date'] ?? ''),
        ];
    }

    /**
     * @param array{ok: bool, message: string, status?: int} $result
     */
    private function occurrenceJson(array $result)
    {
        if (! ($result['ok'] ?? false)) {
            return $this->response
                ->setStatusCode((int) ($result['status'] ?? 404))
                ->setJSON([
                    'ok'    => false,
                    'error' => $result['message'] ?? RecurrenceService::MSG_NOT_FOUND,
                ]);
        }

        return $this->response->setJSON([
            'ok'      => true,
            'message' => $result['message'],
        ]);
    }
}
