<?php

namespace App\Http\Controllers;

use App\Enums\SpecDocumentSectionType;
use App\Http\Requests\ReorderSpecDocumentSections;
use App\Http\Requests\SignOffSpecDocument;
use App\Http\Requests\SpecDocumentRequest;
use App\Http\Requests\StoreSpecDocument;
use App\Http\Requests\UpdateSpecDocument;
use App\Http\Resources\SpecDocumentList;
use App\Http\Resources\SpecDocumentResource;
use App\IntegrationRepository;
use App\Models\SpecDocument;
use App\Models\SpecDocumentSectionTemplate;
use App\SpecDocumentBlueprint;
use Illuminate\Contracts\View\View;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\URL;
use Illuminate\Support\Str;
use Mtc\MercuryDataModels\Tenant;
use Mtc\MercuryDataModels\User;

/**
 * System-level CRUD for site spec documents (central connection). A spec document is the
 * onboarding requirements overview for a new site, previously maintained as a Word
 * template. Documents stay editable until signed off, then become read-only.
 */
class SpecDocumentController extends Controller
{
    use \App\Http\Controllers\Concerns\SyncsSpecDocumentTenantFields;

    public function index(SpecDocumentRequest $request): SpecDocumentList
    {
        $documents = SpecDocument::query()
            ->with('tenant')
            ->withCount([
                'sections',
                'sections as completed_sections_count' => fn ($query) => $query->whereNotNull('completed_at'),
            ])
            ->when(
                $request->filled('tenant_id'),
                fn ($query) => $query->where('tenant_id', $request->input('tenant_id'))
            )
            ->latest()
            ->paginate();

        $documents->through(fn (SpecDocument $document) => [
            'id' => $document->id,
            'tenant_id' => $document->tenant_id,
            'tenant_name' => $document->tenant?->name,
            'title' => $document->title,
            'version' => $document->version,
            'is_signed_off' => $document->isSignedOff(),
            'signed_off_at' => $document->signed_off_at,
            'sections_count' => $document->sections_count,
            'completed_sections_count' => $document->completed_sections_count,
            'updated_at' => $document->updated_at,
        ]);

        return new SpecDocumentList($documents);
    }

    public function show(SpecDocumentRequest $request, SpecDocument $specDocument): SpecDocumentResource
    {
        $specDocument->load(['tenant', 'sections', 'attachments', 'signedOffByUser', 'author']);
        $this->hydrateTenantFields($specDocument);

        return new SpecDocumentResource($specDocument);
    }

    public function store(StoreSpecDocument $request, IntegrationRepository $integrations): SpecDocumentResource
    {
        $specDocument = SpecDocument::query()->create([
            'tenant_id' => $request->input('tenant_id'),
            'title' => $request->input('title'),
            'version' => $request->input('version'),
            'created_by' => Auth::id(),
            'author_id' => Auth::id(),
        ]);

        $templates = SpecDocumentSectionTemplate::query()->get()->pluck('data', 'type');

        foreach (SpecDocumentSectionType::predefined() as $index => $type) {
            $specDocument->sections()->create([
                'type' => $type->value,
                'title' => $type->label(),
                'sort_order' => ($index + 1) * 10,
                'data' => $this->defaultSectionData($type, $integrations, $templates),
            ]);
        }

        return $this->show(app(SpecDocumentRequest::class), $specDocument);
    }

    /**
     * Seed data for a freshly created section. Templatable sections (Introduction, sections
     * 12-15) auto-fill from the central section template; the integrations section is
     * pre-populated with one entry per known integration type (type set, details blank).
     *
     * @param \Illuminate\Support\Collection<string, array<string, mixed>|null> $templates
     * @return array<string, mixed>|null
     */
    private function defaultSectionData(
        SpecDocumentSectionType $type,
        IntegrationRepository $integrations,
        $templates
    ): ?array {
        if ($this->specDocumentBlueprint()->isTemplatable($type->value)) {
            $data = $templates[$type->value] ?? null;

            return is_array($data) ? $data : null;
        }

        if ($type !== SpecDocumentSectionType::INTEGRATIONS) {
            return null;
        }

        $entries = collect($integrations->metadata())
            ->pluck('type')
            ->unique()
            ->map(fn (string $slug) => $this->integrationTypeLabel($slug))
            ->unique()
            ->sort()
            ->values()
            ->map(fn (string $label) => [
                'type' => $label,
                'provider' => null,
                'required' => false,
                'notes' => null,
            ])
            ->all();

        return ['integrations' => $entries];
    }

    public function update(UpdateSpecDocument $request, SpecDocument $specDocument): SpecDocumentResource
    {
        abort_if($specDocument->isSignedOff(), 403, 'Spec document has been signed off and is read-only.');

        $specDocument->update($request->validated());

        return $this->show(app(SpecDocumentRequest::class), $specDocument);
    }

    public function destroy(SpecDocumentRequest $request, SpecDocument $specDocument): SpecDocumentList
    {
        abort_if($specDocument->isSignedOff(), 403, 'Spec document has been signed off and cannot be deleted.');

        $specDocument->delete();

        return $this->index($request);
    }

    public function signOff(SignOffSpecDocument $request, SpecDocument $specDocument): SpecDocumentResource
    {
        abort_if($specDocument->isSignedOff(), 403, 'Spec document has already been signed off.');

        $incompleteSections = $specDocument->sections()->whereNull('completed_at')->count();
        abort_if(
            $incompleteSections > 0,
            422,
            'All sections must be completed before the spec document can be signed off.'
        );

        $specDocument->update([
            'signed_off_at' => now(),
            'signed_off_by' => Auth::id(),
            'sign_off_notes' => $request->input('sign_off_notes'),
        ]);

        return $this->show(app(SpecDocumentRequest::class), $specDocument);
    }

    /**
     * Reorder custom sections. Predefined sections keep their seeded order; the given
     * custom section ids are re-sequenced after the highest predefined sort_order.
     */
    public function reorderSections(
        ReorderSpecDocumentSections $request,
        SpecDocument $specDocument
    ): SpecDocumentResource {
        abort_if($specDocument->isSignedOff(), 403, 'Spec document has been signed off and is read-only.');

        $base = (int) $specDocument->sections()->where('type', '!=', SpecDocumentSectionType::CUSTOM->value)
            ->max('sort_order');

        foreach (array_values($request->input('section_ids')) as $index => $sectionId) {
            $specDocument->sections()
                ->where('type', SpecDocumentSectionType::CUSTOM->value)
                ->whereKey($sectionId)
                ->update(['sort_order' => $base + (($index + 1) * 10)]);
        }

        return $this->show(app(SpecDocumentRequest::class), $specDocument);
    }

    /**
     * A unique signed URL for the branded document preview. The preview route also
     * requires an authenticated mtc admin session, so the link is both unguessable
     * and access-controlled (spec documents are confidential client data).
     */
    public function previewLink(SpecDocumentRequest $request, SpecDocument $specDocument): array
    {
        return [
            'url' => URL::signedRoute('spec-documents.preview', ['spec_document' => $specDocument->id]),
        ];
    }

    /**
     * Render the spec document as an mtc-branded, read-only HTML document.
     */
    public function preview(
        SpecDocumentRequest $request,
        SpecDocument $specDocument,
        SpecDocumentBlueprint $blueprint
    ): View {
        $specDocument->load(['tenant', 'sections', 'author']);
        $this->hydrateTenantFields($specDocument);

        $sections = $specDocument->sections
            ->map(function ($section) use ($blueprint) {
                $type = SpecDocumentSectionType::tryFrom($section->type);

                return [
                    'id' => $section->id,
                    'title' => $section->title,
                    'fields' => $type ? $blueprint->fields($type) : [],
                    'data' => is_array($section->data) ? $section->data : [],
                ];
            })
            ->filter(fn (array $section) => $this->sectionHasContent($section['data']))
            ->values();

        return view('spec-document.preview', [
            'document' => $specDocument,
            'sections' => $sections,
        ]);
    }

    /**
     * Whether a section carries any non-empty value (used to skip empty sections in
     * the document preview).
     */
    private function sectionHasContent(array $data): bool
    {
        foreach ($data as $value) {
            if (is_array($value)) {
                foreach ($value as $item) {
                    if (is_array($item) && $this->sectionHasContent($item)) {
                        return true;
                    }
                    if (!is_array($item) && $item !== null && $item !== '') {
                        return true;
                    }
                }
            } elseif ($value !== null && $value !== '') {
                return true;
            }
        }

        return false;
    }

    /**
     * Section blueprints and select option sources for the admin UI form renderer.
     */
    public function options(
        SpecDocumentRequest $request,
        SpecDocumentBlueprint $blueprint,
        IntegrationRepository $integrations
    ): array {
        $providers = $integrations->metadata();

        // Reader-friendly type labels, and providers grouped by that label so the UI can
        // scope the provider dropdown to the selected type.
        $typeLabels = $providers
            ->pluck('type')
            ->unique()
            ->mapWithKeys(fn (string $slug) => [$slug => $this->integrationTypeLabel($slug)]);

        $providersByType = $providers
            ->groupBy('type')
            ->mapWithKeys(fn ($group, string $slug) => [
                $typeLabels[$slug] => $group->pluck('name')->unique()->sort()->values(),
            ]);

        return [
            'section_types' => $blueprint->schemas(),
            'sites' => Tenant::query()
                ->whereNotIn('id', SpecDocument::query()->pluck('tenant_id'))
                ->orderBy('name')
                ->get(['id', 'name']),
            'users' => User::query()
                ->role('mtc')
                ->orderBy('name')
                ->get(['id', 'name']),
            'sources' => [
                'integration_types' => $typeLabels->values()->sort()->values(),
                'integration_providers_by_type' => $providersByType,
                'stock_providers' => $providers->where('type', 'stock')->pluck('name')->values(),
                'sales_channel_providers' => $providers->where('type', 'sales-channels')->pluck('name')->values(),
            ],
        ];
    }

    /**
     * Human-readable label for an integration type slug.
     */
    private function integrationTypeLabel(string $slug): string
    {
        return [
            'sales-channels' => 'Sales Channels',
            'chat' => 'Live Chat',
            'crm' => 'CRM',
            'stock' => 'Stock',
            'lookup' => 'Lookup',
            'socials' => 'Social Media',
            'valuation' => 'Valuation',
            'image-sync' => 'Image Sync',
            'finance' => 'Finance',
            'vehicle-specs' => 'Vehicle Specs',
            'ai-content' => 'AI Content',
            'newsletters' => 'Newsletters',
            'offer-stock' => 'Offer Stock',
            'sales' => 'Sales',
            'smart-image-management' => 'Smart Image Management',
            'job' => 'Jobs',
            'lease-data' => 'Lease Data',
            'stock-reservation' => 'Stock Reservation',
        ][$slug] ?? Str::headline($slug);
    }
}
