<?php

declare(strict_types=1);

namespace App\Http\Controllers\Project;

use App\Http\Controllers\Concerns\AuthorizesProjectAccess;
use App\Http\Controllers\Controller;
use App\Http\Resources\ProjectSummaryResource;
use App\Models\Project;
use App\Models\ProjectContractSignature;
use App\Models\User;
use App\Notifications\SimpleDatabaseNotification;
use App\Services\OtpService;
use App\Support\ApiResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Hash;

class ProjectContractController extends Controller
{
    use AuthorizesProjectAccess;

    public function show(Project $project)
    {
        $this->authorizeProjectAccess($project);

        $project->loadApiRelations()->load(['contractSignatures.user']);

        $signatures = $project->contractSignatures;

        return ApiResponse::success([
            'project_id' => $project->id,
            'contract_text' => $project->contract_text,
            'status' => $project->status,
            'employer_signed' => $signatures->where('role', ProjectContractSignature::ROLE_EMPLOYER)->contains(fn ($s) => $s->isSigned()),
            'producer_signed' => $signatures->where('role', ProjectContractSignature::ROLE_PRODUCER)->contains(fn ($s) => $s->isSigned()),
            'signatures' => $signatures->map(fn (ProjectContractSignature $signature) => [
                'id' => $signature->id,
                'user_id' => $signature->user_id,
                'role' => $signature->role,
                'signed_at' => $signature->signed_at?->toISOString(),
                'user' => $signature->user ? [
                    'id' => $signature->user->id,
                    'full_name' => $signature->user->getFilamentName(),
                ] : null,
            ])->values(),
            'project' => new ProjectSummaryResource($project),
        ]);
    }

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

        $user = $request->user();
        $role = $this->resolveRole($user, $project);

        if (! $role) {
            return ApiResponse::forbidden('شما مجاز به امضای قرارداد این پروژه نیستید.');
        }

        if (! $user->cellphone) {
            return ApiResponse::badRequest('شماره موبایل برای ارسال کد تأیید ثبت نشده است.');
        }

        if (blank($project->contract_text)) {
            return ApiResponse::badRequest('متن قرارداد برای این پروژه ثبت نشده است.');
        }

        $code = $otpService->generateOtp();
        $otpService->sendOtp($user->cellphone, $code);

        $signature = ProjectContractSignature::firstOrNew([
            'project_id' => $project->id,
            'user_id' => $user->id,
            'role' => $role,
        ]);

        if ($signature->isSigned()) {
            return ApiResponse::badRequest('قرارداد قبلاً توسط شما امضا شده است.');
        }

        $signature->fill([
            'code_hash' => Hash::make((string) $code),
            'code_expires_at' => now()->addMinutes(10),
            'signed_at' => null,
        ]);
        $signature->save();

        return ApiResponse::success([
            'expires_at' => $signature->code_expires_at?->toISOString(),
            'role' => $role,
        ], 'کد تأیید قرارداد ارسال شد.');
    }

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

        $request->validate([
            'code' => ['required', 'string', 'max:20'],
        ], [
            'code.required' => 'کد تأیید الزامی است.',
        ]);

        $user = $request->user();
        $role = $this->resolveRole($user, $project);

        if (! $role) {
            return ApiResponse::forbidden('شما مجاز به امضای قرارداد این پروژه نیستید.');
        }

        $signature = ProjectContractSignature::query()
            ->where('project_id', $project->id)
            ->where('user_id', $user->id)
            ->where('role', $role)
            ->first();

        if (! $signature || ! $signature->code_hash) {
            return ApiResponse::badRequest('ابتدا درخواست ارسال کد تأیید را ثبت کنید.');
        }

        if ($signature->isSigned()) {
            // Admin may have signed earlier while status stayed unsigned (old advance logic).
            if ($user->hasRole('admin') && in_array($project->status, [
                Project::STATUS_UNSIGNED,
                Project::STATUS_PENDING,
                Project::STATUS_WAITING,
            ], true)) {
                $this->advanceProjectStatus($project, $user);
                $project->loadApiRelations()->load(['contractSignatures.user']);

                return ApiResponse::success([
                    'signed_at' => $signature->signed_at?->toISOString(),
                    'role' => $role,
                    'project_status' => $project->fresh()->status,
                    'project' => new ProjectSummaryResource($project->fresh()),
                ], 'وضعیت پروژه به‌روزرسانی شد.');
            }

            return ApiResponse::badRequest('قرارداد قبلاً امضا شده است.');
        }

        if ($signature->code_expires_at && $signature->code_expires_at->isPast()) {
            return ApiResponse::badRequest('کد تأیید منقضی شده است.');
        }

        if (! Hash::check($request->input('code'), $signature->code_hash)) {
            return ApiResponse::badRequest('کد تأیید نامعتبر است.');
        }

        $signature->update([
            'signed_at' => now(),
            'code_hash' => null,
            'code_expires_at' => null,
        ]);

        $this->advanceProjectStatus($project, $user);

        $project->loadApiRelations()->load(['contractSignatures.user']);

        return ApiResponse::success([
            'signed_at' => $signature->signed_at?->toISOString(),
            'role' => $role,
            'project_status' => $project->fresh()->status,
            'project' => new ProjectSummaryResource($project->fresh()),
        ], 'قرارداد با موفقیت امضا شد.');
    }

    private function resolveRole(User $user, Project $project): ?string
    {
        $project->loadMissing('producer');

        if ($project->producer && $project->producer->user_id === $user->id) {
            return ProjectContractSignature::ROLE_PRODUCER;
        }

        if ($user->id === $project->user_id) {
            return ProjectContractSignature::ROLE_EMPLOYER;
        }

        if ($user->hasRole('admin')) {
            $signatures = $project->contractSignatures()->get();
            $producerSigned = $signatures
                ->where('role', ProjectContractSignature::ROLE_PRODUCER)
                ->contains(fn (ProjectContractSignature $s) => $s->isSigned());
            $employerSigned = $signatures
                ->where('role', ProjectContractSignature::ROLE_EMPLOYER)
                ->contains(fn (ProjectContractSignature $s) => $s->isSigned());

            // Sign as whichever party is still missing so admin can unblock the contract.
            if (! $producerSigned && $project->producer_id) {
                return ProjectContractSignature::ROLE_PRODUCER;
            }

            if (! $employerSigned) {
                return ProjectContractSignature::ROLE_EMPLOYER;
            }

            return ProjectContractSignature::ROLE_EMPLOYER;
        }

        return null;
    }

    private function advanceProjectStatus(Project $project, ?User $actor = null): void
    {
        $project->refresh();
        $project->loadMissing('producer.user');

        $signatures = $project->contractSignatures()->get();
        $producerSigned = $signatures
            ->where('role', ProjectContractSignature::ROLE_PRODUCER)
            ->contains(fn (ProjectContractSignature $s) => $s->isSigned());
        $employerSigned = $signatures
            ->where('role', ProjectContractSignature::ROLE_EMPLOYER)
            ->contains(fn (ProjectContractSignature $s) => $s->isSigned());

        $bothSigned = $producerSigned && ($employerSigned || blank($project->user_id));
        $adminActor = $actor?->hasRole('admin') ?? false;

        // Admin confirmation can finalize an unsigned/pending/waiting contract.
        if ($adminActor && in_array($project->status, [
            Project::STATUS_UNSIGNED,
            Project::STATUS_PENDING,
            Project::STATUS_WAITING,
        ], true)) {
            $project->update(['status' => Project::STATUS_ACTIVE]);
            $this->notifyContractSigned($project);

            return;
        }

        if ($bothSigned && in_array($project->status, [
            Project::STATUS_UNSIGNED,
            Project::STATUS_PENDING,
            Project::STATUS_WAITING,
        ], true)) {
            $project->update(['status' => Project::STATUS_ACTIVE]);
            $this->notifyContractSigned($project);

            return;
        }

        // Producer signed first → wait for employer confirmation.
        if (
            $producerSigned
            && ! $employerSigned
            && in_array($project->status, [Project::STATUS_UNSIGNED, Project::STATUS_PENDING], true)
        ) {
            $project->update(['status' => Project::STATUS_WAITING]);
        }
    }

    private function notifyContractSigned(Project $project): void
    {
        $recipient = $project->producer?->user;
        if (! $recipient) {
            return;
        }

        $recipient->notify(new SimpleDatabaseNotification(
            'امضای قرارداد پروژه',
            "قرارداد پروژه «{$project->name}» نهایی و پروژه فعال شد.",
            ['type' => 'project_contract', 'project_id' => $project->id]
        ));
    }
}
