<?php

namespace App\Http\Controllers;

use App\Services\NotionRenderer;
use App\Services\NotionService;
use App\Support\NotionTask;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Cache;
use Illuminate\View\View;

/**
 * The Manifest — the public documentation knowledge base site.
 *
 * Public, no login: home (/) shows every documented sprint as a vessel
 * (Vessel = Sprint, Container = Card); /novidades is the condensed
 * catalog view; each document lives at /doc/{id}. Technical documentation
 * lives directly in Notion (team folders/guidelines) — it is generated
 * there by the pipeline but never mirrored on this site.
 *
 * The only password gate left ({@see \App\Http\Middleware\ManifestDevAuth})
 * protects the /releases/* admin tooling, unrelated to doc browsing.
 */
class ManifestController extends Controller
{
    private const CACHE_TTL = 600; // seconds

    public function __construct(
        private readonly NotionService  $notion,
        private readonly NotionRenderer $renderer,
    ) {}

    // ── Home: Vessel = Sprint, Container = Card ──────────────────────────────

    public function index(): View
    {
        return view('manifest.index', [
            'vessels' => $this->vessels(),
        ]);
    }

    public function show(string $pageId): View
    {
        return view('manifest.show', [
            'doc' => $this->renderDoc($pageId),
        ]);
    }

    // ── Central de Novidades ─────────────────────────────────────────────────
    // Versão resumida em catálogo — segunda forma de navegar os mesmos
    // documentos, acessível pelo menu.

    public function updates(): View
    {
        return view('manifest.novidades', [
            'groups' => $this->documentedTasks(),
        ]);
    }

    public function vesselShow(string $vessel): View
    {
        return view('manifest.vessel', [
            'vessel' => $this->vesselData($vessel),
        ]);
    }

    // ── Admin login (gates /releases only — no docs area behind it) ──────────

    public function loginForm(): View
    {
        return view('manifest.login');
    }

    public function login(Request $request): RedirectResponse
    {
        $request->validate(['password' => 'required|string']);

        if (! hash_equals((string) config('services.manifest.dev_password'), $request->input('password'))) {
            return back()->withErrors(['password' => 'Senha incorreta.']);
        }

        $request->session()->regenerate(); // prevent session fixation
        $request->session()->put('manifest_dev_ok', true);

        return redirect()->intended(route('manifest.index'));
    }

    public function logout(Request $request): RedirectResponse
    {
        $request->session()->forget('manifest_dev_ok');

        return redirect()->route('manifest.index');
    }

    // ── Helpers ──────────────────────────────────────────────────────────────

    /**
     * All published tasks grouped by category (project / issue / bug).
     * Each entry carries the internal doc route + task metadata.
     */
    private function documentedTasks(): array
    {
        if (config('services.manifest.demo')) {
            return \App\Support\DemoManifest::groups();
        }

        try {
            return Cache::remember('manifest.groups', self::CACHE_TTL, fn () => $this->buildGroups());
        } catch (\Throwable $e) {
            // Notion unreachable or integration lacks access — show empty state
            // on the public site instead of a 500.
            \Illuminate\Support\Facades\Log::error('ManifestController: failed to fetch tasks', [
                'error' => $e->getMessage(),
            ]);
            return [];
        }
    }

    private function buildGroups(): array
    {
        // Human review gate: only tasks marked "Published" reach the public site.
        $tasks = $this->notion->queryDocumentedTasks(statuses: ['Published']);

        $groups = ['project' => [], 'issue' => [], 'bug' => []];

        foreach ($tasks as $task) {
            $category = NotionTask::category($task);
            $docId    = NotionTask::pageIdFromUrl(NotionTask::releaseNoteUrl($task));

            if (! $docId) {
                continue; // task marked Published but missing the release note link
            }

            $groups[$category][] = [
                'title'  => $this->publicDocTitle($docId, $task),
                'doc_id' => $docId,
                'date'   => NotionTask::lastEdited($task),
            ];
        }

        return array_filter($groups);
    }

    /**
     * Vessels (sprints) with their containers (documented cards), for the
     * Pátio do Porto. Falls back to an empty yard if Notion is unreachable.
     */
    private function vessels(): array
    {
        if (config('services.manifest.demo')) {
            return \App\Support\DemoManifest::vessels();
        }

        try {
            return Cache::remember('manifest.vessels', self::CACHE_TTL, fn () => $this->buildVessels());
        } catch (\Throwable $e) {
            \Illuminate\Support\Facades\Log::error('ManifestController: failed to fetch vessels', [
                'error' => $e->getMessage(),
            ]);
            return [];
        }
    }

    private function buildVessels(): array
    {
        $sprintTitles = $this->notion->sprintTitles();

        $tasks = $this->notion->queryDocumentedTasks(statuses: ['Published']);

        $bySprintId = [];
        foreach ($tasks as $task) {
            foreach ($task['properties']['Sprint']['relation'] ?? [] as $relation) {
                $bySprintId[$relation['id']][] = $task;
            }
        }

        $vessels = [];

        foreach ($bySprintId as $sprintId => $sprintTasks) {
            $containers = array_values(array_filter(
                array_map(fn (array $task) => $this->containerFromTask($task), $sprintTasks)
            ));

            // Tasks da sprint ainda sem documentação — aparecem como "pendentes".
            $totalInSprint = count($this->notion->queryTasksBySprint($sprintId));

            $vessels[] = [
                'key'        => $sprintId,
                'label'      => $sprintTitles[$sprintId] ?? 'Sprint',
                'voyage'     => null,
                'status'     => null,
                'period'     => null,
                'documented' => count($containers),
                'total'      => max($totalInSprint, count($containers)),
                'containers' => $containers,
            ];
        }

        return $vessels;
    }

    private function containerFromTask(array $task): ?array
    {
        $docId = NotionTask::pageIdFromUrl(NotionTask::releaseNoteUrl($task));

        if (! $docId) {
            return null;
        }

        return [
            'number'   => NotionTask::cardNumber($task) ?? NotionTask::cleanTitle($task),
            'title'    => $this->publicDocTitle($docId, $task),
            'category' => NotionTask::category($task),
            'doc_id'   => $docId,
            'date'     => NotionTask::lastEdited($task),
        ];
    }

    private function vesselData(string $vesselKey): array
    {
        if (config('services.manifest.demo')) {
            $vessel = \App\Support\DemoManifest::vessel($vesselKey);
            abort_unless((bool) $vessel, 404);

            return $vessel;
        }

        try {
            $vessel = Cache::remember(
                "manifest.vessel.{$vesselKey}",
                self::CACHE_TTL,
                fn () => collect($this->buildVessels())->firstWhere('key', $vesselKey)
            );
        } catch (\Throwable $e) {
            \Illuminate\Support\Facades\Log::error('ManifestController: failed to fetch vessel', [
                'vessel' => $vesselKey,
                'error'  => $e->getMessage(),
            ]);
            abort(404);
        }

        abort_unless((bool) $vessel, 404);

        return $vessel;
    }

    /**
     * Internal task names often carry client/user names — the public site
     * shows the Release Note page title (written for end users) instead.
     */
    private function publicDocTitle(string $docId, array $task): string
    {
        try {
            $title = NotionTask::pageTitle($this->notion->getPage($docId));

            return $title !== '' ? $title : NotionTask::cleanTitle($task);
        } catch (\Throwable) {
            return NotionTask::cleanTitle($task);
        }
    }

    /**
     * Fetch + render a doc page, refusing pages outside the Release Notes
     * section (prevents rendering arbitrary Notion pages by guessing IDs).
     */
    private function renderDoc(string $pageId): array
    {
        if (config('services.manifest.demo')) {
            $doc = \App\Support\DemoManifest::doc($pageId);
            abort_unless((bool) $doc, 404);

            // Linha do tempo da task — só disponível no modo demo por ora.
            $doc['journey'] = \App\Support\DemoManifest::journey($pageId);

            return $doc;
        }

        return Cache::remember("manifest.doc.{$pageId}", self::CACHE_TTL, function () use ($pageId) {
            $page = $this->notion->getPage($pageId);

            $parentId       = $page['parent']['page_id'] ?? null;
            $expectedParent = config('services.notion.releases_parent_page_id');

            if (! $expectedParent || ! $parentId
                || str_replace('-', '', $parentId) !== str_replace('-', '', $expectedParent)) {
                abort(404);
            }

            return [
                'title' => NotionTask::pageTitle($page),
                'html'  => $this->renderer->render($pageId),
                'date'  => $page['last_edited_time'] ?? null,
            ];
        });
    }
}
