<?php

declare(strict_types=1);

namespace App\Http\Controllers\Project;

use App\Http\Controllers\Concerns\AuthorizesProjectAccess;
use App\Http\Controllers\Controller;
use App\Http\Requests\Project\FlagProjectMediaRequest;
use App\Http\Requests\Project\StoreProjectDocumentRequest;
use App\Http\Requests\Project\StoreProjectGalleryRequest;
use App\Http\Resources\MediaItemResource;
use App\Models\MediaCategory;
use App\Models\MediaFlag;
use App\Models\MediaItem;
use App\Models\Project;
use App\Support\ApiResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;

class ProjectMediaController extends Controller
{
    use AuthorizesProjectAccess;

    public function storeGallery(StoreProjectGalleryRequest $request, Project $project)
    {
        $this->authorizeProjectAccess($project);

        $item = $this->createMedia($project, $request->validated(), MediaItem::TYPE_IMAGE, 'gallery');

        return ApiResponse::created(
            new MediaItemResource($item->load(['category', 'subcategory', 'project'])),
            'آیتم گالری ثبت شد.'
        );
    }

    public function updateGallery(StoreProjectGalleryRequest $request, Project $project, MediaItem $media)
    {
        $this->authorizeProjectAccess($project);
        $this->ensureBelongs($project, $media, [MediaItem::TYPE_IMAGE, MediaItem::TYPE_VIDEO]);

        $media = $this->updateMedia($media, $request->validated(), 'gallery');

        return ApiResponse::success(
            new MediaItemResource($media->load(['category', 'subcategory', 'project'])),
            'آیتم گالری ویرایش شد.'
        );
    }

    public function storeDocument(StoreProjectDocumentRequest $request, Project $project)
    {
        $this->authorizeProjectAccess($project);

        $item = $this->createMedia($project, $request->validated(), MediaItem::TYPE_DOCUMENT, 'document');

        return ApiResponse::created(
            new MediaItemResource($item->load(['category', 'subcategory', 'project'])),
            'سند ثبت شد.'
        );
    }

    public function updateDocument(StoreProjectDocumentRequest $request, Project $project, MediaItem $media)
    {
        $this->authorizeProjectAccess($project);
        $this->ensureBelongs($project, $media, [MediaItem::TYPE_DOCUMENT]);

        $media = $this->updateMedia($media, $request->validated(), 'document');

        return ApiResponse::success(
            new MediaItemResource($media->load(['category', 'subcategory', 'project'])),
            'سند ویرایش شد.'
        );
    }

    public function flag(FlagProjectMediaRequest $request, Project $project, MediaItem $media)
    {
        $this->authorizeProjectAccess($project);
        $this->ensureBelongs($project, $media);

        MediaFlag::query()->updateOrCreate(
            [
                'media_item_id' => $media->id,
                'user_id' => Auth::id(),
            ],
            ['reason' => $request->string('reason')->toString()]
        );

        return ApiResponse::success(message: 'گزارش تخلف ثبت شد.');
    }

    public function producerStats(Request $request, Project $project)
    {
        $this->authorizeProjectAccess($project);

        $base = MediaItem::query()->where('project_id', $project->id);

        $gallery = (clone $base)->whereIn('type', [MediaItem::TYPE_IMAGE, MediaItem::TYPE_VIDEO]);
        $documents = (clone $base)->where('type', MediaItem::TYPE_DOCUMENT);

        return ApiResponse::success([
            'gallery_count' => (clone $gallery)->count(),
            'gallery_approved' => (clone $gallery)->where('status', MediaItem::STATUS_APPROVED)->count(),
            'gallery_draft' => (clone $gallery)->where('is_draft', true)->count(),
            'documents_count' => (clone $documents)->count(),
            'documents_approved' => (clone $documents)->where('status', MediaItem::STATUS_APPROVED)->count(),
            'documents_draft' => (clone $documents)->where('is_draft', true)->count(),
            'flagged_count' => MediaFlag::query()
                ->whereHas('mediaItem', fn ($q) => $q->where('project_id', $project->id))
                ->count(),
            'rating_avg' => round((float) (
                MediaItem::query()
                    ->where('project_id', $project->id)
                    ->whereIn('type', [MediaItem::TYPE_IMAGE, MediaItem::TYPE_VIDEO])
                    ->withAvg('ratings', 'score')
                    ->get()
                    ->avg('ratings_avg_score') ?? 0
            ), 2),
        ]);
    }

    /**
     * @param  array<string, mixed>  $data
     */
    private function createMedia(Project $project, array $data, string $defaultType, string $categoryType): MediaItem
    {
        $type = $data['type'] ?? $defaultType;
        $isDraft = (bool) ($data['is_draft'] ?? false);
        [$lat, $lng] = $this->parseGps($data);
        $categoryId = $this->resolveCategoryId($data, $categoryType);
        $path = $this->normalizeUrlPath((string) $data['url']);

        return MediaItem::create([
            'project_id' => $project->id,
            'uploader_type' => $this->resolveUploaderType(),
            'uploader_id' => Auth::id(),
            'type' => $type,
            'category_id' => $categoryId,
            'name' => $data['title'],
            'file_path' => $path,
            'files' => [$path],
            'description' => $data['description'] ?? null,
            'notes' => $data['notes'] ?? null,
            'gps_lat' => $lat,
            'gps_lng' => $lng,
            'location_name' => $data['location_name'] ?? null,
            'status' => $isDraft ? MediaItem::STATUS_PENDING : MediaItem::STATUS_PENDING,
            'is_draft' => $isDraft,
            'contract_number' => $data['document_number'] ?? null,
            'document_category' => $data['document_category'] ?? ($data['category'] ?? null),
            'company_name' => $data['company_name'] ?? null,
            'contact_phone' => $data['company_phone'] ?? null,
            'start_at' => $data['valid_from'] ?? null,
            'end_at' => $data['valid_until'] ?? null,
            'created_by' => Auth::id(),
        ]);
    }

    /**
     * @param  array<string, mixed>  $data
     */
    private function updateMedia(MediaItem $media, array $data, string $categoryType): MediaItem
    {
        [$lat, $lng] = $this->parseGps($data);
        $categoryId = $this->resolveCategoryId($data, $categoryType);
        $path = isset($data['url']) ? $this->normalizeUrlPath((string) $data['url']) : $media->file_path;
        $isDraft = array_key_exists('is_draft', $data) ? (bool) $data['is_draft'] : $media->is_draft;

        $media->update(array_filter([
            'type' => $data['type'] ?? $media->type,
            'category_id' => $categoryId ?? $media->category_id,
            'name' => $data['title'] ?? $media->name,
            'file_path' => $path,
            'files' => $path ? [$path] : $media->files,
            'description' => $data['description'] ?? $media->description,
            'notes' => $data['notes'] ?? $media->notes,
            'gps_lat' => $lat ?? $media->gps_lat,
            'gps_lng' => $lng ?? $media->gps_lng,
            'location_name' => $data['location_name'] ?? $media->location_name,
            'is_draft' => $isDraft,
            'contract_number' => $data['document_number'] ?? $media->contract_number,
            'document_category' => $data['document_category'] ?? ($data['category'] ?? $media->document_category),
            'company_name' => $data['company_name'] ?? $media->company_name,
            'contact_phone' => $data['company_phone'] ?? $media->contact_phone,
            'start_at' => $data['valid_from'] ?? $media->start_at,
            'end_at' => $data['valid_until'] ?? $media->end_at,
        ], fn ($v) => $v !== null));

        return $media->fresh();
    }

    /**
     * @param  list<string>|null  $types
     */
    private function ensureBelongs(Project $project, MediaItem $media, ?array $types = null): void
    {
        if ((int) $media->project_id !== (int) $project->id) {
            abort(404, 'رسانه متعلق به این پروژه نیست.');
        }

        if ($types !== null && ! in_array($media->type, $types, true)) {
            abort(422, 'نوع رسانه با این مسیر هم‌خوانی ندارد.');
        }
    }

    /**
     * @param  array<string, mixed>  $data
     * @return array{0: ?float, 1: ?float}
     */
    private function parseGps(array $data): array
    {
        if (isset($data['gps_lat'], $data['gps_lng'])) {
            return [(float) $data['gps_lat'], (float) $data['gps_lng']];
        }

        $gps = $data['gps'] ?? null;
        if (is_string($gps) && str_contains($gps, ',')) {
            [$lat, $lng] = array_map('trim', explode(',', $gps, 2));

            return [is_numeric($lat) ? (float) $lat : null, is_numeric($lng) ? (float) $lng : null];
        }

        return [null, null];
    }

    /**
     * @param  array<string, mixed>  $data
     */
    private function resolveCategoryId(array $data, string $type): ?int
    {
        if (! empty($data['category_id'])) {
            return (int) $data['category_id'];
        }

        $name = $data['document_category'] ?? $data['category'] ?? null;
        if (! is_string($name) || $name === '') {
            return null;
        }

        $category = MediaCategory::query()->firstOrCreate(
            ['name' => $name, 'type' => $type],
            ['sort_order' => 0]
        );

        return $category->id;
    }

    private function normalizeUrlPath(string $url): string
    {
        $appUrl = rtrim((string) config('app.url'), '/');
        if ($appUrl !== '' && str_starts_with($url, $appUrl)) {
            return ltrim(substr($url, strlen($appUrl)), '/');
        }

        return $url;
    }

    private function resolveUploaderType(): string
    {
        $user = Auth::user();
        if ($user?->hasRole('admin')) {
            return MediaItem::UPLOADER_ADMIN;
        }
        if ($user?->producer || $user?->hasRole('producer')) {
            return MediaItem::UPLOADER_PRODUCER;
        }

        return MediaItem::UPLOADER_EMPLOYER;
    }
}
