<?php

declare(strict_types=1);

namespace App\Http\Controllers\Report;

use App\Http\Controllers\Concerns\AuthorizesProjectAccess;
use App\Http\Controllers\Controller;
use App\Http\Requests\Report\GrowthReportRequest;
use App\Http\Requests\Report\GrowthWorkflowRequest;
use App\Http\Requests\Report\IndexGrowthReportRequest;
use App\Http\Resources\ReportResource;
use App\Models\Project;
use App\Models\Report;
use App\Models\ReportInvoiceItem;
use App\Models\ReportItem;
use App\Notifications\SimpleDatabaseNotification;
use App\Support\ApiResponse;
use Exception;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Gate;

class GrowthReportController extends Controller
{
    use AuthorizesProjectAccess;

    public function index(IndexGrowthReportRequest $request)
    {
        Gate::authorize('viewAny', Report::class);

        $query = Report::query()
            ->where('type', Report::TYPE_GROWTH)
            ->with(['project', 'producer.user', 'user', 'items.product', 'invoiceItems']);

        $this->restrictVisibility($query, $request->user());
        $this->applyFilters($query, $request);

        $perPage = min($request->integer('per_page', 15), 50);

        return ReportResource::collection(
            $request->boolean('noPaginate') ? $query->get() : $query->paginate($perPage)
        );
    }

    public function overview(Request $request)
    {
        Gate::authorize('viewAny', Report::class);

        $projectsQuery = $this->accessibleProjectsQuery()
            ->with(['category', 'product', 'producer']);

        if ($request->filled('project')) {
            $term = $request->string('project');
            $projectsQuery->where(function (Builder $q) use ($term): void {
                $q->where('name', 'like', "%{$term}%")
                    ->orWhere('code', 'like', "%{$term}%");
            });
        }

        if ($request->filled('category_id')) {
            $projectsQuery->where('category_id', $request->integer('category_id'));
        }

        $projects = $projectsQuery->get();

        $data = $projects->map(function (Project $project) {
            $reports = Report::query()
                ->where('project_id', $project->id)
                ->where('type', Report::TYPE_GROWTH)
                ->with('items')
                ->get();

            $approved = $reports->where('workflow_status', Report::WORKFLOW_APPROVED)->values();
            $latest = $approved->sortByDesc(fn (Report $r) => $r->report_at ?? $r->date)->first();

            return [
                'project' => [
                    'id' => $project->id,
                    'code' => $project->code,
                    'name' => $project->name,
                    'status' => $project->status,
                ],
                'reports_count' => $reports->count(),
                'pending_count' => $reports->where('workflow_status', Report::WORKFLOW_PENDING)->count(),
                'approved_count' => $approved->count(),
                'rejected_count' => $reports->where('workflow_status', Report::WORKFLOW_REJECTED)->count(),
                'needs_edit_count' => $reports->where('workflow_status', Report::WORKFLOW_NEEDS_EDIT)->count(),
                'current_weight' => $latest?->itemsAverage(),
                'target' => $latest?->metrics['target'] ?? null,
                'latest_report_at' => ($latest?->report_at ?? $latest?->date)?->toDateString(),
            ];
        });

        return ApiResponse::success(['projects' => $data->values()]);
    }

    public function store(GrowthReportRequest $request)
    {
        Gate::authorize('create', Report::class);

        if ($request->filled('project_id')) {
            $this->authorizeProjectAccess(Project::findOrFail($request->integer('project_id')));
        }

        try {
            DB::beginTransaction();

            $isDraft = $request->boolean('is_draft') || $request->input('status') === 'draft';
            $data = $this->payload($request, $isDraft);
            $items = $data['items'] ?? [];
            $invoiceItems = $data['invoice_items'] ?? null;
            unset($data['items'], $data['invoice_items'], $data['is_draft']);

            $report = Report::create($data);
            $this->syncItems($report, $items);
            $this->syncInvoiceItems($report, $invoiceItems);

            DB::commit();
        } catch (Exception $exception) {
            DB::rollBack();

            return ApiResponse::response(message: $exception->getMessage(), status: 419);
        }

        return ApiResponse::created(
            new ReportResource($report->load(['items.product', 'invoiceItems', 'project', 'producer', 'user'])),
            $isDraft ? 'پیش‌نویس گزارش رشد ذخیره شد.' : 'گزارش رشد ثبت و برای تأیید ارسال شد.'
        );
    }

    public function show(Report $report)
    {
        $this->ensureGrowth($report);
        Gate::authorize('view', $report);

        return new ReportResource($report->load([
            'items.product',
            'invoiceItems',
            'project',
            'producer.user',
            'user',
            'product',
        ]));
    }

    public function update(GrowthReportRequest $request, Report $report)
    {
        $this->ensureGrowth($report);
        Gate::authorize('update', $report);

        if (in_array($report->workflow_status, [Report::WORKFLOW_APPROVED], true) && ! $request->user()->hasRole('admin')) {
            return ApiResponse::forbidden('گزارش تأییدشده قابل ویرایش نیست.');
        }

        try {
            DB::beginTransaction();

            $isDraft = $request->boolean('is_draft') || $request->input('status') === 'draft';
            $data = $this->payload($request, $isDraft, $report);
            $items = $data['items'] ?? null;
            $invoiceItems = $data['invoice_items'] ?? null;
            unset($data['items'], $data['invoice_items'], $data['is_draft']);

            if ($report->workflow_status === Report::WORKFLOW_NEEDS_EDIT && ! $isDraft) {
                $data['workflow_status'] = Report::WORKFLOW_PENDING;
                $data['admin_reason'] = null;
            }

            $report->update($data);

            if ($items !== null) {
                $this->syncItems($report, $items);
            }
            if ($invoiceItems !== null) {
                $this->syncInvoiceItems($report, $invoiceItems);
            }

            DB::commit();
        } catch (Exception $exception) {
            DB::rollBack();

            return ApiResponse::response(message: $exception->getMessage(), status: 419);
        }

        return ApiResponse::success(
            new ReportResource($report->fresh()->load(['items.product', 'invoiceItems', 'project', 'producer', 'user'])),
            'گزارش رشد به‌روزرسانی شد.'
        );
    }

    public function submit(Report $report)
    {
        $this->ensureGrowth($report);
        Gate::authorize('update', $report);

        if ($report->status === 'published' && $report->workflow_status === Report::WORKFLOW_PENDING) {
            return ApiResponse::badRequest('گزارش قبلاً برای تأیید ارسال شده است.');
        }

        $report->update([
            'status' => 'published',
            'workflow_status' => Report::WORKFLOW_PENDING,
            'admin_reason' => null,
            'updated_by' => Auth::id(),
        ]);

        return ApiResponse::success(
            new ReportResource($report->fresh()->load(['items.product', 'project', 'producer'])),
            'گزارش برای تأیید ارسال شد.'
        );
    }

    public function approve(GrowthWorkflowRequest $request, Report $report)
    {
        $this->ensureGrowth($report);
        $this->authorizeAdmin();

        $report->update([
            'workflow_status' => Report::WORKFLOW_APPROVED,
            'status' => 'published',
            'admin_reason' => null,
            'updated_by' => Auth::id(),
        ]);

        $this->notifySender($report, 'تأیید گزارش رشد', "گزارش رشد «{$report->title}» تأیید شد.");

        return ApiResponse::success(
            new ReportResource($report->fresh()->load(['items.product', 'project', 'producer'])),
            'گزارش رشد تأیید شد.'
        );
    }

    public function reject(GrowthWorkflowRequest $request, Report $report)
    {
        $this->ensureGrowth($report);
        $this->authorizeAdmin();

        $reason = $request->input('admin_reason') ?? $request->input('reason');
        if (blank($reason)) {
            return ApiResponse::badRequest('ذکر دلیل رد الزامی است.');
        }

        $report->update([
            'workflow_status' => Report::WORKFLOW_REJECTED,
            'admin_reason' => $reason,
            'updated_by' => Auth::id(),
        ]);

        $this->notifySender($report, 'رد گزارش رشد', "گزارش رشد «{$report->title}» رد شد. دلیل: {$reason}");

        return ApiResponse::success(
            new ReportResource($report->fresh()->load(['items.product', 'project', 'producer'])),
            'گزارش رشد رد شد.'
        );
    }

    public function needsEdit(GrowthWorkflowRequest $request, Report $report)
    {
        $this->ensureGrowth($report);
        $this->authorizeAdmin();

        $reason = $request->input('admin_reason') ?? $request->input('reason');
        if (blank($reason)) {
            return ApiResponse::badRequest('ذکر دلیل نیاز به ویرایش الزامی است.');
        }

        $report->update([
            'workflow_status' => Report::WORKFLOW_NEEDS_EDIT,
            'admin_reason' => $reason,
            'updated_by' => Auth::id(),
        ]);

        $this->notifySender($report, 'نیاز به ویرایش گزارش رشد', "گزارش رشد «{$report->title}» نیاز به ویرایش دارد. دلیل: {$reason}");

        return ApiResponse::success(
            new ReportResource($report->fresh()->load(['items.product', 'project', 'producer'])),
            'گزارش برای ویرایش به فرستنده ارجاع شد.'
        );
    }

    public function destroy(Report $report)
    {
        $this->ensureGrowth($report);
        Gate::authorize('delete', $report);

        $report->delete();

        return ApiResponse::success(message: 'گزارش رشد حذف شد.');
    }

    public function history(Request $request, Project $project)
    {
        $this->authorizeProjectAccess($project);
        $project->loadMissing('producer');

        $query = Report::query()
            ->where('project_id', $project->id)
            ->where('type', Report::TYPE_GROWTH)
            ->with(['items.product', 'producer.user', 'user', 'invoiceItems']);

        if (! $request->user()->hasRole('admin')) {
            $user = $request->user();
            $isProducer = $project->producer && $project->producer->user_id === $user->id;
            if (! $isProducer && $user->id !== $project->user_id && $user->id !== $project->created_by) {
                $query->where('workflow_status', Report::WORKFLOW_APPROVED);
            } elseif (! $isProducer) {
                $query->where(function (Builder $q) use ($user): void {
                    $q->where('workflow_status', Report::WORKFLOW_APPROVED)
                        ->orWhere('created_by', $user->id)
                        ->orWhere('user_id', $user->id);
                });
            }
        }

        if ($request->filled('workflow_status')) {
            $query->where('workflow_status', $request->string('workflow_status'));
        }

        if ($request->filled('from')) {
            $from = $request->date('from');
            $query->where(function (Builder $q) use ($from): void {
                $q->whereDate('report_at', '>=', $from)
                    ->orWhere(function (Builder $inner) use ($from): void {
                        $inner->whereNull('report_at')->whereDate('date', '>=', $from);
                    });
            });
        }

        if ($request->filled('to')) {
            $to = $request->date('to');
            $query->where(function (Builder $q) use ($to): void {
                $q->whereDate('report_at', '<=', $to)
                    ->orWhere(function (Builder $inner) use ($to): void {
                        $inner->whereNull('report_at')->whereDate('date', '<=', $to);
                    });
            });
        }

        $reports = $query->get()->sortByDesc(fn (Report $r) => $r->report_at ?? $r->date)->values();
        $approved = $reports->where('workflow_status', Report::WORKFLOW_APPROVED)->values();

        $chart = $approved->map(function (Report $report) {
            $date = ($report->report_at ?? $report->date)?->toDateString();

            return [
                'date' => $date,
                'month' => $date ? substr($date, 0, 7) : null,
                'avg' => $report->itemsAverage(),
                'min' => $report->itemsMin(),
                'max' => $report->itemsMax(),
                'report_id' => $report->id,
            ];
        })->values();

        $latest = $approved->first();
        $previous = $approved->skip(1)->first();
        $dailyGrowth = null;
        if ($latest && $previous && $latest->itemsAverage() !== null && $previous->itemsAverage() !== null) {
            $days = max(1, ($latest->report_at ?? $latest->date)?->diffInDays($previous->report_at ?? $previous->date) ?: 1);
            $dailyGrowth = round(($latest->itemsAverage() - $previous->itemsAverage()) / $days, 3);
        }

        $target = $latest?->metrics['target']
            ?? $project->metrics['target'] ?? null;

        return ApiResponse::success([
            'project' => [
                'id' => $project->id,
                'code' => $project->code,
                'name' => $project->name,
                'quantity' => $project->quantity,
            ],
            'summary' => [
                'current_weight' => $latest?->itemsAverage(),
                'daily_growth' => $dailyGrowth ?? ($latest?->metrics['daily_growth'] ?? null),
                'target' => $target,
                'latest_min' => $latest?->itemsMin(),
                'latest_max' => $latest?->itemsMax(),
            ],
            'chart' => $chart,
            'monthly_chart' => $chart->groupBy('month')->map(fn ($rows, $month) => [
                'month' => $month,
                'avg' => round((float) collect($rows)->pluck('avg')->filter()->avg(), 3),
                'min' => collect($rows)->pluck('min')->filter()->min(),
                'max' => collect($rows)->pluck('max')->filter()->max(),
            ])->values(),
            'tabs' => [
                'current' => $latest?->itemsAverage(),
                'target' => $target,
                'daily_growth' => $dailyGrowth,
            ],
            'reports' => ReportResource::collection($reports),
        ]);
    }

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

        $reports = Report::query()
            ->where('project_id', $project->id)
            ->where('type', Report::TYPE_GROWTH)
            ->where('workflow_status', Report::WORKFLOW_APPROVED)
            ->with(['items' => fn ($q) => $q->where('product_id', $productId)->with('product')])
            ->orderBy('report_at')
            ->orderBy('date')
            ->get();

        $series = [];
        foreach ($reports as $report) {
            foreach ($report->items as $item) {
                $series[] = [
                    'date' => ($report->report_at ?? $report->date)?->toDateString(),
                    'report_id' => $report->id,
                    'product_id' => $item->product_id,
                    'min_value' => $item->min_value !== null ? (float) $item->min_value : null,
                    'max_value' => $item->max_value !== null ? (float) $item->max_value : null,
                    'avg_value' => $item->resolveAverage(),
                    'value' => $item->value !== null ? (float) $item->value : null,
                    'unit' => $item->unit ?? $report->unit,
                    'action_note' => $item->action_note,
                ];
            }
        }

        return ApiResponse::success([
            'project_id' => $project->id,
            'product_id' => $productId,
            'series' => $series,
        ]);
    }

    public function updateItemAction(Request $request, Report $report, ReportItem $reportItem)
    {
        $this->ensureGrowth($report);
        Gate::authorize('update', $report);

        if ($reportItem->report_id !== $report->id) {
            return ApiResponse::notFound('آیتم در این گزارش یافت نشد.');
        }

        $request->validate([
            'action_note' => ['required', 'string', 'max:2000'],
        ], [
            'action_note.required' => 'متن دستور/رسیدگی الزامی است.',
        ]);

        $reportItem->update(['action_note' => $request->input('action_note')]);

        $producerUser = $report->producer?->user;
        if ($producerUser) {
            $producerUser->notify(new SimpleDatabaseNotification(
                'دستور رسیدگی محصول در گزارش رشد',
                $request->input('action_note'),
                [
                    'type' => 'growth_item_action',
                    'report_id' => $report->id,
                    'item_id' => $reportItem->id,
                    'product_id' => $reportItem->product_id,
                ]
            ));
        }

        return ApiResponse::success(
            new \App\Http\Resources\ReportItemResource($reportItem->load('product')),
            'دستور رسیدگی ثبت شد.'
        );
    }

    private function payload(GrowthReportRequest $request, bool $isDraft, ?Report $existing = null): array
    {
        $data = $request->safe()->except(['is_draft']);
        $data['type'] = Report::TYPE_GROWTH;
        $data['created_by'] = $existing?->created_by ?? Auth::id();
        $data['updated_by'] = Auth::id();
        $data['user_id'] = $data['user_id'] ?? $existing?->user_id ?? Auth::id();
        $data['report_at'] = $data['report_at'] ?? $data['date'];
        $data['title'] = $data['title'] ?? ('گزارش رشد ' . ($data['date'] ?? now()->toDateString()));
        $data['report_type'] = $data['report_type'] ?? Report::GROWTH_TYPE_WEIGHING;
        $data['status'] = $isDraft ? 'draft' : 'published';
        $data['workflow_status'] = $isDraft
            ? ($existing?->workflow_status ?? Report::WORKFLOW_PENDING)
            : Report::WORKFLOW_PENDING;
        $data['create_invoice'] = $request->boolean('create_invoice');

        if (! $request->filled('producer_id') && $request->filled('project_id')) {
            $data['producer_id'] = Project::find($request->integer('project_id'))?->producer_id;
        }

        return $data;
    }

    private function syncItems(Report $report, array $items): void
    {
        $report->items()->delete();

        foreach ($items as $index => $item) {
            $min = $item['min_value'] ?? null;
            $max = $item['max_value'] ?? null;
            $value = $item['value'] ?? null;
            $avg = $item['avg_value'] ?? null;

            if ($avg === null && $min !== null && $max !== null) {
                $avg = ((float) $min + (float) $max) / 2;
            } elseif ($avg === null && $value !== null) {
                $avg = $value;
            }

            ReportItem::create([
                'report_id' => $report->id,
                'product_id' => $item['product_id'] ?? null,
                'group_name' => $item['group_name'] ?? null,
                'min_value' => $min,
                'max_value' => $max,
                'value' => $value,
                'avg_value' => $avg,
                'unit' => $item['unit'] ?? $report->unit,
                'action_note' => $item['action_note'] ?? null,
                'sort_order' => $index,
            ]);
        }
    }

    private function syncInvoiceItems(Report $report, ?array $invoiceItems): void
    {
        if ($invoiceItems === null) {
            return;
        }

        $report->invoiceItems()->delete();
        foreach ($invoiceItems as $index => $invoiceItem) {
            ReportInvoiceItem::create([
                'report_id' => $report->id,
                'title' => $invoiceItem['title'],
                'amount' => $invoiceItem['amount'] ?? 0,
                'sort_order' => $index,
            ]);
        }
    }

    private function restrictVisibility(Builder $query, $user): void
    {
        if ($user->hasRole('admin')) {
            return;
        }

        $query->where(function (Builder $scope) use ($user): void {
            $scope->where('user_id', $user->id)
                ->orWhere('created_by', $user->id)
                ->orWhereHas('project', function (Builder $projectQuery) use ($user): void {
                    $projectQuery->where('user_id', $user->id)
                        ->orWhere('created_by', $user->id)
                        ->orWhereHas('producer', fn (Builder $p) => $p->where('user_id', $user->id));
                });
        });
    }

    private function applyFilters(Builder $query, IndexGrowthReportRequest $request): void
    {
        if ($request->filled('project_id')) {
            $query->where('project_id', $request->integer('project_id'));
        }

        if ($request->filled('project')) {
            $term = $request->string('project');
            $query->whereHas('project', function (Builder $q) use ($term): void {
                $q->where('name', 'like', "%{$term}%")
                    ->orWhere('code', 'like', "%{$term}%");
            });
        }

        if ($request->filled('product_id')) {
            $productId = $request->integer('product_id');
            $query->where(function (Builder $q) use ($productId): void {
                $q->where('product_id', $productId)
                    ->orWhereHas('items', fn (Builder $i) => $i->where('product_id', $productId));
            });
        }

        if ($request->filled('product')) {
            $term = $request->string('product');
            $query->whereHas('items.product', fn (Builder $q) => $q->where('name', 'like', "%{$term}%"));
        }

        if ($request->filled('producer_id')) {
            $query->where('producer_id', $request->integer('producer_id'));
        }

        if ($request->filled('workflow_status')) {
            $query->where('workflow_status', $request->string('workflow_status'));
        }

        if ($request->filled('status')) {
            $query->where('status', $request->string('status'));
        }

        if ($request->filled('from')) {
            $from = $request->date('from');
            $query->where(function (Builder $q) use ($from): void {
                $q->whereDate('report_at', '>=', $from)
                    ->orWhere(function (Builder $inner) use ($from): void {
                        $inner->whereNull('report_at')->whereDate('date', '>=', $from);
                    });
            });
        }

        if ($request->filled('to')) {
            $to = $request->date('to');
            $query->where(function (Builder $q) use ($to): void {
                $q->whereDate('report_at', '<=', $to)
                    ->orWhere(function (Builder $inner) use ($to): void {
                        $inner->whereNull('report_at')->whereDate('date', '<=', $to);
                    });
            });
        }
    }

    private function ensureGrowth(Report $report): void
    {
        if (! $report->isGrowthReport()) {
            abort(404, 'گزارش رشد یافت نشد.');
        }
    }

    private function authorizeAdmin(): void
    {
        if (! Auth::user()?->hasRole('admin')) {
            abort(403, 'فقط ادمین مجاز است.');
        }
    }

    private function notifySender(Report $report, string $title, string $body): void
    {
        $user = $report->user ?? $report->createdBy;
        if ($user) {
            $user->notify(new SimpleDatabaseNotification($title, $body, [
                'type' => 'growth_workflow',
                'report_id' => $report->id,
                'workflow_status' => $report->workflow_status,
            ]));
        }
    }
}
