<?php

namespace App\Http\Controllers;

use App\Exceptions\RoboflowWorkflowException;
use App\Exceptions\GroqAnalysisException;
use App\Models\DamageFinding;
use App\Models\Inspection;
use App\Models\InspectionCollection;
use App\Models\InspectionWorkspace;
use App\Services\GroqAnalysisClient;
use App\Services\InspectionScanRecorder;
use App\Services\RoboflowWorkflowClient;
use Illuminate\Contracts\View\View;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;

class InspectionController extends Controller
{
    public function index(Request $request): View
    {
        $status = $request->string('status')->toString();
        $severity = $request->string('severity')->toString();
        $search = trim($request->string('search')->toString());
        $workspace = $this->activeWorkspace($request);
        $workspaceId = (int) $workspace->id;
        $collectionId = $request->integer('collection_id');
        if ($collectionId > 0) {
            $collectionBelongsToWorkspace = InspectionCollection::query()
                ->where('user_id', $request->user()->id)
                ->where('inspection_workspace_id', $workspaceId)
                ->where('id', $collectionId)
                ->exists();

            if (! $collectionBelongsToWorkspace) {
                $collectionId = 0;
            }
        }

        $baseQuery = Inspection::query()
            ->whereHas('collection', fn ($query) => $query->where('inspection_workspace_id', $workspaceId));
        if ($collectionId > 0) {
            $baseQuery->where('inspection_collection_id', $collectionId);
        }

        $inspections = (clone $baseQuery)
            ->with(['report', 'damageFindings', 'collection'])
            ->withCount('damageFindings')
            ->when($status !== '', fn ($query) => $query->where('status', $status))
            ->when($severity !== '', function ($query) use ($severity) {
                $query->whereHas('damageFindings', fn ($findingQuery) => $findingQuery->where('severity', $severity));
            })
            ->when($search !== '', function ($query) use ($search) {
                $query->where(function ($nestedQuery) use ($search) {
                    $nestedQuery
                        ->where('property_name', 'like', "%{$search}%")
                        ->orWhere('property_address', 'like', "%{$search}%")
                        ->orWhereHas('collection', function ($collectionQuery) use ($search) {
                            $collectionQuery
                                ->where('property_name', 'like', "%{$search}%")
                                ->orWhere('room_name', 'like', "%{$search}%");
                        });
                });
            })
            ->latest('scheduled_at')
            ->paginate(8)
            ->withQueryString();

        $findingQuery = DamageFinding::query()
            ->whereHas('inspection.collection', fn ($query) => $query->where('inspection_workspace_id', $workspaceId))
            ->when($collectionId > 0, fn ($query) => $query->whereHas('inspection', fn ($inspectionQuery) => $inspectionQuery->where('inspection_collection_id', $collectionId)));

        $stats = [
            'total' => (clone $baseQuery)->count(),
            'completed' => (clone $baseQuery)->where('status', 'completed')->count(),
            'review' => (clone $baseQuery)->where('status', 'review')->count(),
            'scanning' => (clone $baseQuery)->where('status', 'scanning')->count(),
            'findings' => (clone $findingQuery)->count(),
            'repairCost' => (clone $baseQuery)->sum('estimated_cost'),
        ];

        $recentFindings = DamageFinding::query()
            ->with('inspection.collection')
            ->whereHas('inspection.collection', fn ($query) => $query->where('inspection_workspace_id', $workspaceId))
            ->when($collectionId > 0, fn ($query) => $query->whereHas('inspection', fn ($inspectionQuery) => $inspectionQuery->where('inspection_collection_id', $collectionId)))
            ->latest()
            ->limit(5)
            ->get();
        $collections = InspectionCollection::query()
            ->where('user_id', $request->user()->id)
            ->where('inspection_workspace_id', $workspaceId)
            ->withCount('inspections')
            ->orderBy('property_name')
            ->orderBy('room_name')
            ->get();
        $workspaces = InspectionWorkspace::query()
            ->where('user_id', $request->user()->id)
            ->withCount('collections')
            ->orderBy('name')
            ->get()
            ->map(function (InspectionWorkspace $workspace) {
                $workspace->inspections_count = Inspection::query()
                    ->whereHas('collection', fn ($query) => $query->where('inspection_workspace_id', $workspace->id))
                    ->count();
                $workspace->estimated_cost_sum = Inspection::query()
                    ->whereHas('collection', fn ($query) => $query->where('inspection_workspace_id', $workspace->id))
                    ->sum('estimated_cost');

                return $workspace;
            });

        return view('inspections.index', [
            'inspections' => $inspections,
            'recentFindings' => $recentFindings,
            'stats' => $stats,
            'workspaces' => $workspaces,
            'activeWorkspace' => $workspace,
            'collections' => $collections,
            'filters' => [
                'workspace_id' => $workspaceId,
                'status' => $status,
                'severity' => $severity,
                'search' => $search,
                'collection_id' => $collectionId,
            ],
        ]);
    }

    public function scan(Request $request, RoboflowWorkflowClient $roboflow, InspectionScanRecorder $recorder, GroqAnalysisClient $groq): JsonResponse
    {
        $validated = $request->validate([
            'images' => ['required', 'array', 'min:1', 'max:20'],
            'images.*' => ['required', 'image', 'max:10240'],
            'roboflow_model' => ['nullable', 'string', 'in:'.implode(',', RoboflowWorkflowClient::modelKeys())],
            'currency' => ['nullable', 'in:IDR,USD'],
            'workspace_id' => ['nullable', 'integer', 'exists:inspection_workspaces,id'],
            'workspace_name' => ['nullable', 'string', 'max:120'],
            'collection_id' => ['nullable', 'integer', 'exists:inspection_collections,id'],
            'collection_type' => ['nullable', 'in:house,room,collection'],
            'collection_name' => ['nullable', 'string', 'max:120'],
            'property_name' => ['nullable', 'string', 'max:160'],
            'property_address' => ['nullable', 'string', 'max:255'],
            'room_name' => ['nullable', 'string', 'max:120'],
            'output_language' => ['nullable', 'in:id,en'],
            'detail_level' => ['nullable', 'in:quick,breakdown,scope'],
            'price_research' => ['nullable', 'boolean'],
            'research_depth' => ['nullable', 'in:basic,standard,deep'],
            'source_link_mode' => ['nullable', 'in:direct,search,both'],
            'labor_pricing_mode' => ['nullable', 'in:auto,daily,contract,monthly'],
            'labor_detail_level' => ['nullable', 'in:standard,detailed,forensic'],
            'marketplaces' => ['nullable', 'array', 'max:10'],
            'marketplaces.*' => ['string', 'in:tokopedia,shopee,ace,depo_bangunan,tukang_com,kanggo,sejasa,hwb'],
            'include_service_recommendations' => ['nullable', 'boolean'],
            'models' => ['nullable', 'array', 'max:3'],
            'models.*' => ['string', 'in:'.implode(',', array_keys(GroqAnalysisClient::MODELS))],
            'report_points' => ['nullable', 'array'],
            'report_points.*' => ['string', 'in:materials,labor,urgency,safety,preventive_maintenance'],
            'temperature' => ['nullable', 'numeric', 'min:0', 'max:1'],
            'max_tokens' => ['nullable', 'integer', 'min:512', 'max:4096'],
        ]);

        $settings = [
            'currency' => $validated['currency'] ?? 'IDR',
            'roboflow_model' => $validated['roboflow_model'] ?? config('services.roboflow.default_model', 'modelv1-low'),
            'workspace_id' => $validated['workspace_id'] ?? null,
            'workspace_name' => $validated['workspace_name'] ?? null,
            'collection_id' => $validated['collection_id'] ?? null,
            'collection_type' => $validated['collection_type'] ?? 'room',
            'collection_name' => $validated['collection_name'] ?? null,
            'property_name' => $validated['property_name'] ?? null,
            'property_address' => $validated['property_address'] ?? null,
            'room_name' => $validated['room_name'] ?? null,
            'output_language' => $validated['output_language'] ?? app()->getLocale(),
            'detail_level' => $validated['detail_level'] ?? 'breakdown',
            'price_research' => (bool) ($validated['price_research'] ?? false),
            'research_depth' => $validated['research_depth'] ?? 'standard',
            'source_link_mode' => $validated['source_link_mode'] ?? 'both',
            'labor_pricing_mode' => $validated['labor_pricing_mode'] ?? 'auto',
            'labor_detail_level' => $validated['labor_detail_level'] ?? 'detailed',
            'marketplaces' => $validated['marketplaces'] ?? ['tokopedia', 'shopee', 'tukang_com', 'kanggo', 'sejasa', 'hwb'],
            'include_service_recommendations' => (bool) ($validated['include_service_recommendations'] ?? true),
            'models' => array_slice($validated['models'] ?? [], 0, 3),
            'report_points' => $validated['report_points'] ?? ['materials', 'labor', 'urgency', 'safety'],
            'temperature' => (float) ($validated['temperature'] ?? 0),
            'max_tokens' => (int) ($validated['max_tokens'] ?? 2048),
        ];
        $settings = $this->normalizeScanWorkspaceSettings($request, $settings);

        try {
            $results = $roboflow->scanImagesWithModel($validated['images'], $settings['roboflow_model']);
            $validIndexes = collect($results)
                ->keys()
                ->filter(fn (int $index) => (int) ($results[$index]['detection_count'] ?? 0) > 0)
                ->values();
            $validResults = $validIndexes
                ->map(fn (int $index) => $results[$index])
                ->values()
                ->all();
            $validImages = $validIndexes
                ->map(fn (int $index) => $validated['images'][$index])
                ->values()
                ->all();
            $failedResults = collect($results)
                ->filter(fn (array $result) => (int) ($result['detection_count'] ?? 0) === 0)
                ->values()
                ->all();
            $acceptedDetectionCount = collect($validResults)
                ->sum(fn (array $result) => (int) ($result['detection_count'] ?? 0));
            $rawDetectionCount = collect($results)
                ->sum(fn (array $result) => (int) ($result['raw_detection_count'] ?? $result['detection_count'] ?? 0));

            if ($validResults === []) {
                return response()->json([
                    'status' => 'failed',
                    'message' => 'Roboflow did not detect any home inspection issue in the uploaded images.',
                    'results' => [],
                    'failed_results' => $failedResults,
                    'scan_summary' => [
                        'total' => count($results),
                        'detected' => 0,
                        'failed' => count($failedResults),
                    ],
                ], 422);
            }

            $settings['failed_images'] = collect($failedResults)
                ->map(fn (array $result) => [
                    'file' => $result['file'] ?? 'image',
                    'reason' => $result['failed_reason'] ?? 'No Roboflow detections.',
                    'detection_count' => (int) ($result['detection_count'] ?? 0),
                    'raw_detection_count' => (int) ($result['raw_detection_count'] ?? $result['detection_count'] ?? 0),
                ])
                ->values()
                ->all();
            $settings['roboflow_detection_count'] = $acceptedDetectionCount;
            $settings['roboflow_raw_detection_count'] = $rawDetectionCount;

            $inspection = $recorder->record($request->user(), $validResults, $validImages, $settings);

            if ($settings['models'] !== []) {
                $analyses = $groq->analyze($inspection, $validResults, $settings);
                $inspection = $recorder->attachGroqAnalyses($inspection, $analyses, $settings);
            }
        } catch (RoboflowWorkflowException $exception) {
            return response()->json([
                'message' => $exception->getMessage(),
            ], 502);
        } catch (GroqAnalysisException $exception) {
            return response()->json([
                'message' => $exception->getMessage(),
            ], 502);
        }

        return response()->json([
            'status' => $failedResults === [] ? 'completed' : 'partial',
            'results' => $validResults,
            'failed_results' => $failedResults,
            'groq_analyses' => $inspection->groq_analysis_outputs ?? [],
            'inspection' => [
                'id' => $inspection->id,
                'property_name' => $inspection->property_name,
                'status' => $inspection->status,
                'confidence' => (float) $inspection->ai_confidence,
                'findings_count' => $inspection->damageFindings->count(),
                'detection_count' => $acceptedDetectionCount,
                'raw_detection_count' => $rawDetectionCount,
                'report_number' => $inspection->report?->report_number,
                'report_url' => route('reports.show', $inspection->report),
                'collection' => $inspection->collection ? [
                    'id' => $inspection->collection->id,
                    'name' => $inspection->collection->displayName(),
                    'workspace_id' => $inspection->collection->inspection_workspace_id,
                    'workspace_name' => $inspection->collection->workspace?->name,
                    'property_name' => $inspection->collection->property_name,
                    'room_name' => $inspection->collection->room_name,
                ] : null,
            ],
            'scan_summary' => [
                'total' => count($results),
                'detected' => count($validResults),
                'failed' => count($failedResults),
                'detections' => $acceptedDetectionCount,
                'raw_detections' => $rawDetectionCount,
            ],
            'output_keys' => collect($validResults)
                ->flatMap(fn (array $result) => $result['output_keys'])
                ->unique()
                ->values(),
        ]);
    }

    private function activeWorkspace(Request $request): InspectionWorkspace
    {
        $requestedId = $request->integer('workspace_id');

        if ($requestedId > 0) {
            $workspace = InspectionWorkspace::query()
                ->where('user_id', $request->user()->id)
                ->find($requestedId);

            if ($workspace !== null) {
                return $workspace;
            }
        }

        return InspectionWorkspace::query()->firstOrCreate(
            [
                'user_id' => $request->user()->id,
                'name' => 'Default Workspace',
            ],
            [
                'default_currency' => 'IDR',
                'metadata' => ['created_from' => 'controller'],
            ]
        );
    }

    private function normalizeScanWorkspaceSettings(Request $request, array $settings): array
    {
        $userId = $request->user()->id;

        if (! empty($settings['workspace_id'])) {
            $workspace = InspectionWorkspace::query()
                ->where('user_id', $userId)
                ->find($settings['workspace_id']);

            if ($workspace === null) {
                $settings['workspace_id'] = null;
            }
        }

        if (! empty($settings['collection_id'])) {
            $collectionQuery = InspectionCollection::query()
                ->where('user_id', $userId)
                ->where('id', $settings['collection_id']);

            if (! empty($settings['workspace_id'])) {
                $collectionQuery->where('inspection_workspace_id', $settings['workspace_id']);
            }

            $collection = $collectionQuery->first();

            if ($collection === null) {
                $settings['collection_id'] = null;
            } else {
                $settings['workspace_id'] = $collection->inspection_workspace_id;
            }
        }

        return $settings;
    }
}
