<?php
declare(strict_types=1);

namespace App\Controllers\Web;

use App\Lib\Pdf;
use App\Middleware\AuthSession;
use App\Middleware\TenantScope;
use App\Models\Asset;
use App\Models\Attachment;
use App\Models\Role;
use App\Models\Round;
use App\Models\Tenant;
use Core\Controller;
use Core\Session;
use Core\Tool;

final class RoundsController extends Controller
{
    public function middleware(): array
    {
        return [AuthSession::class, TenantScope::class];
    }

    private function tenantId(): int { return (int) ($_SESSION['tenant_id'] ?? 0); }

    private function canManage(): bool
    {
        $role = Session::user()['role_code'] ?? '';
        return in_array($role, [Role::ADMIN, Role::EXECUTIVE, Role::PROJECT_MANAGER], true);
    }

    public function index(): void
    {
        $tenantId = $this->tenantId();
        $this->render('rounds/index', [
            'templates'   => Round::templatesForTenant($tenantId),
            'recent'      => Round::recentRuns($tenantId, 10),
            'dueStatus'   => Round::dueTodayStatus($tenantId),
            'canManage'   => $this->canManage(),
            'flash'       => Session::flash('rounds_msg'),
        ]);
    }

    public function createTemplate(): void
    {
        if (!$this->canManage()) redirect('/Rounds');
        $this->render('rounds/template-form', [
            'template' => null,
            'csrf'     => Tool::generateCsrf(),
        ]);
    }

    public function editTemplate(int $id): void
    {
        if (!$this->canManage()) redirect('/Rounds');
        $tpl = Round::findTemplate($this->tenantId(), $id);
        if (!$tpl) { Session::flash('rounds_msg', 'Round not found.'); redirect('/Rounds'); }
        $this->render('rounds/template-form', [
            'template' => $tpl,
            'stops'    => Round::stopsFor($id),
            'assets'   => Asset::optionsForTenant($this->tenantId()),
            'csrf'     => Tool::generateCsrf(),
        ]);
    }

    public function saveTemplate(): void
    {
        if (($_SERVER['REQUEST_METHOD'] ?? '') !== 'POST') redirect('/Rounds');
        if (!$this->canManage() || !Tool::checkCsrf($_POST['_csrf'] ?? null)) redirect('/Rounds');
        $tenantId = $this->tenantId();
        $id = (int) ($_POST['round_template_id'] ?? 0);
        if (trim((string) ($_POST['name'] ?? '')) === '') {
            Session::flash('rounds_msg', 'Name is required.');
            redirect($id > 0 ? "/Rounds/editTemplate/{$id}" : '/Rounds/createTemplate');
        }
        if ($id > 0) {
            Round::updateTemplate($tenantId, $id, $_POST);
            Session::flash('rounds_msg', 'Round updated.');
        } else {
            $id = Round::createTemplate($tenantId, $_POST);
            Session::flash('rounds_msg', 'Round created. Now add stops.');
        }
        redirect('/Rounds/editTemplate/' . $id);
    }

    public function deleteTemplate(int $id): void
    {
        if (!$this->canManage()) redirect('/Rounds');
        Round::softDeleteTemplate($this->tenantId(), $id);
        Session::flash('rounds_msg', 'Round deleted.');
        redirect('/Rounds');
    }

    public function addStop(int $templateId): void
    {
        if (($_SERVER['REQUEST_METHOD'] ?? '') !== 'POST') redirect('/Rounds');
        if (!$this->canManage() || !Tool::checkCsrf($_POST['_csrf'] ?? null)) redirect('/Rounds');
        if (!Round::findTemplate($this->tenantId(), $templateId)) redirect('/Rounds');
        if (!empty($_POST['asset_id'])) {
            Round::addStop($templateId, $_POST);
            Session::flash('rounds_msg', 'Stop added.');
        }
        redirect('/Rounds/editTemplate/' . $templateId);
    }

    public function deleteStop(int $stopId): void
    {
        if (!$this->canManage()) redirect('/Rounds');
        // Find the parent template so we redirect back to the editor.
        $row = \Core\DB::one(
            'SELECT s.round_template_id
               FROM round_template_stops s
               JOIN round_templates rt ON rt.round_template_id = s.round_template_id
              WHERE s.stop_id = :id AND rt.tenant_id = :t',
            ['id' => $stopId, 't' => $this->tenantId()]
        );
        if (!$row) redirect('/Rounds');
        Round::deleteStop($stopId);
        Session::flash('rounds_msg', 'Stop removed.');
        redirect('/Rounds/editTemplate/' . (int) $row['round_template_id']);
    }

    /** Start a new run by an operator. Returns to the walk view. */
    public function start(int $templateId): void
    {
        $tenantId  = $this->tenantId();
        $operator  = (int) (Session::user()['user_id'] ?? 0);
        if (!Round::findTemplate($tenantId, $templateId)) redirect('/Rounds');
        $runId = Round::startRun($tenantId, $templateId, $operator);
        redirect('/Rounds/run/' . $runId);
    }

    public function run(int $runId): void
    {
        $tenantId = $this->tenantId();
        $run = Round::findRun($tenantId, $runId);
        if (!$run) { Session::flash('rounds_msg', 'Run not found.'); redirect('/Rounds'); }
        $stops = Round::runStopsFor($runId);

        // One query for every photo across every stop in the run, then group
        // by owner_id so the view can render each stop's strip in O(1).
        $photosByStop = [];
        $stopIds = array_map(static fn($s) => (int) $s['run_stop_id'], $stops);
        if (!empty($stopIds)) {
            $ph = implode(',', array_fill(0, count($stopIds), '?'));
            $rows = \Core\DB::all(
                "SELECT * FROM attachments
                  WHERE tenant_id = ? AND owner_type = 'round_run_stop'
                    AND owner_id IN ({$ph}) AND deleted_at IS NULL
                  ORDER BY created_at",
                array_merge([$tenantId], $stopIds)
            );
            foreach ($rows as $r) {
                $photosByStop[(int) $r['owner_id']][] = $r;
            }
        }

        $this->render('rounds/run', [
            'run'          => $run,
            'stops'        => $stops,
            'photosByStop' => $photosByStop,
            'csrf'         => Tool::generateCsrf(),
            'flash'        => Session::flash('rounds_msg'),
        ]);
    }

    public function trends(int $templateId): void
    {
        $tenantId = $this->tenantId();
        $tpl = Round::findTemplate($tenantId, $templateId);
        if (!$tpl) { Session::flash('rounds_msg', 'Round not found.'); redirect('/Rounds'); }
        $this->render('rounds/trends', [
            'template' => $tpl,
            'stops'    => Round::readingHistoryForTemplate($tenantId, $templateId, 30),
        ]);
    }

    /**
     * Stream an audit-ready PDF report for the run — reading table + photo
     * thumbnails per stop. Uses the same mPDF wrapper the WO/incident reports
     * use, plus a per-run photo-grouping map for O(1) render in the template.
     */
    public function pdf(int $runId): void
    {
        $tenantId = $this->tenantId();
        $run = Round::findRun($tenantId, $runId);
        if (!$run) { http_response_code(404); echo 'Not found'; return; }
        $stops = Round::runStopsFor($runId);

        $photosByStop = [];
        $stopIds = array_map(static fn($s) => (int) $s['run_stop_id'], $stops);
        if (!empty($stopIds)) {
            $ph = implode(',', array_fill(0, count($stopIds), '?'));
            $rows = \Core\DB::all(
                "SELECT * FROM attachments
                  WHERE tenant_id = ? AND owner_type = 'round_run_stop'
                    AND owner_id IN ({$ph}) AND deleted_at IS NULL
                  ORDER BY created_at",
                array_merge([$tenantId], $stopIds)
            );
            foreach ($rows as $r) {
                $photosByStop[(int) $r['owner_id']][] = $r;
            }
        }

        $tenant = Tenant::findById($tenantId);
        ob_start();
        require ROOT . '/app/Views/pdf/round_run.php';
        $html = ob_get_clean();

        Pdf::stream($html, 'ROUND-' . $runId);
    }

    /**
     * Bundle every photo captured on this run into a single ZIP for the
     * compliance folder. File naming: stop-{sortOrder}-{assetName}-{n}.jpg
     * so an auditor can browse without opening the app.
     */
    public function photosZip(int $runId): void
    {
        $tenantId = $this->tenantId();
        $run = Round::findRun($tenantId, $runId);
        if (!$run) { http_response_code(404); echo 'Not found'; return; }
        $stops = Round::runStopsFor($runId);

        $stopIds = array_map(static fn($s) => (int) $s['run_stop_id'], $stops);
        if (empty($stopIds)) { http_response_code(404); echo 'No photos'; return; }

        $ph = implode(',', array_fill(0, count($stopIds), '?'));
        $atts = \Core\DB::all(
            "SELECT * FROM attachments
              WHERE tenant_id = ? AND owner_type = 'round_run_stop'
                AND owner_id IN ({$ph}) AND deleted_at IS NULL
              ORDER BY owner_id, created_at",
            array_merge([$tenantId], $stopIds)
        );
        if (empty($atts)) { http_response_code(404); echo 'No photos'; return; }

        // Fast lookup: run_stop_id → (sort_order, asset_name) for filenames.
        $stopMeta = [];
        foreach ($stops as $s) {
            $stopMeta[(int) $s['run_stop_id']] = [
                'sort'  => (int) $s['sort_order'],
                'asset' => (string) $s['asset_name'],
            ];
        }

        $tmpZip = tempnam(sys_get_temp_dir(), 'round_photos_') . '.zip';
        $zip = new \ZipArchive();
        if ($zip->open($tmpZip, \ZipArchive::CREATE | \ZipArchive::OVERWRITE) !== true) {
            http_response_code(500); echo 'Cannot create zip'; return;
        }

        $counters = []; // per-stop counter so file names stay unique
        foreach ($atts as $a) {
            $abs = Attachment::absolutePath($a['file_name']);
            if (!is_file($abs)) continue;
            $meta = $stopMeta[(int) $a['owner_id']] ?? ['sort' => 0, 'asset' => 'unknown'];
            $counters[$a['owner_id']] = ($counters[$a['owner_id']] ?? 0) + 1;
            $n = $counters[$a['owner_id']];
            $slug = strtolower(preg_replace('/[^a-z0-9]+/i', '-', $meta['asset']));
            $slug = trim($slug, '-') ?: 'stop';
            $ext  = $a['file_ext'] ?? pathinfo($a['original_name'], PATHINFO_EXTENSION) ?: 'jpg';
            $inner = sprintf('stop-%02d-%s/photo-%02d.%s', $meta['sort'], $slug, $n, $ext);
            $zip->addFile($abs, $inner);
        }
        $zip->close();

        $filename = 'round-' . $runId . '-photos.zip';
        header('Content-Type: application/zip');
        header('Content-Disposition: attachment; filename="' . $filename . '"');
        header('Content-Length: ' . filesize($tmpZip));
        readfile($tmpZip);
        @unlink($tmpZip);
        exit;
    }

    public function checkStop(int $runStopId): void
    {
        if (($_SERVER['REQUEST_METHOD'] ?? '') !== 'POST') redirect('/Rounds');
        if (!Tool::checkCsrf($_POST['_csrf'] ?? null)) redirect('/Rounds');
        $tenantId = $this->tenantId();
        // Grab the run_id so we can redirect back to the walk view.
        $row = \Core\DB::one(
            'SELECT rrs.run_id
               FROM round_run_stops rrs
               JOIN round_runs rr ON rr.run_id = rrs.run_id
              WHERE rrs.run_stop_id = :id AND rr.tenant_id = :t',
            ['id' => $runStopId, 't' => $tenantId]
        );
        if (!$row) redirect('/Rounds');
        Round::checkOffStop($runStopId, $_POST);
        redirect('/Rounds/run/' . (int) $row['run_id']);
    }

    public function complete(int $runId): void
    {
        if (($_SERVER['REQUEST_METHOD'] ?? '') !== 'POST') redirect('/Rounds/run/' . $runId);
        if (!Tool::checkCsrf($_POST['_csrf'] ?? null)) redirect('/Rounds/run/' . $runId);
        Round::completeRun($this->tenantId(), $runId, $_POST['notes'] ?? null);
        Session::flash('rounds_msg', 'Round completed.');
        redirect('/Rounds');
    }

    public function abandon(int $runId): void
    {
        if (($_SERVER['REQUEST_METHOD'] ?? '') !== 'POST') redirect('/Rounds/run/' . $runId);
        if (!Tool::checkCsrf($_POST['_csrf'] ?? null)) redirect('/Rounds/run/' . $runId);
        Round::abandonRun($this->tenantId(), $runId);
        Session::flash('rounds_msg', 'Round abandoned.');
        redirect('/Rounds');
    }
}
