<?php

namespace App\Http\Controllers\Api;

use App\Http\Controllers\Controller;
use App\Models\Applicant;
use App\Models\Interview;
use App\Models\JobPosting;
use App\Models\User;
use App\Services\Recruitment\ApplicantScopeService;
use App\Services\Recruitment\ApplicantStageService;
use App\Services\Recruitment\RecruitmentHomeService;
use App\Services\Recruitment\RecruitmentNavService;
use App\Services\Recruitment\RecruitmentSlaService;
use App\Services\Recruitment\ReferralDuplicateService;
use App\Services\Recruitment\ReferralProgressService;
use App\Support\ResolvesPrivateUploads;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Validation\Rule;

class RecruitmentController extends Controller
{
    use ResolvesPrivateUploads;

    public function __construct(
        protected RecruitmentNavService $nav,
        protected ApplicantScopeService $scope,
        protected ReferralProgressService $progress,
        protected RecruitmentHomeService $home,
        protected ApplicantStageService $stageService,
        protected ReferralDuplicateService $duplicates,
        protected RecruitmentSlaService $sla
    ) {}

    public function home(): JsonResponse
    {
        $user = Auth::user();
        abort_unless($user instanceof User && $this->nav->canAccessModule($user), 403);

        return response()->json([
            'zone' => $this->nav->hasHrAccess($user) ? 'hr' : 'staff',
            'home_path' => parse_url($this->nav->homeUrl($user), PHP_URL_PATH),
            'tabs' => $this->nav->mobileTabs($user),
            'can_refer' => $this->nav->canRefer($user),
            'can_track_referrals' => $this->nav->canTrackReferrals($user),
            'has_hr_access' => $this->nav->hasHrAccess($user),
        ]);
    }

    public function openRoles(Request $request): JsonResponse
    {
        $user = Auth::user();
        abort_unless($user instanceof User && ($this->nav->canRefer($user) || $this->nav->hasHrAccess($user)), 403);

        $jobs = JobPosting::query()
            ->with('department:id,name')
            ->where('status', 'active')
            ->orderByDesc('posted_at')
            ->paginate(min(50, (int) $request->query('per_page', 15)));

        return response()->json([
            'data' => collect($jobs->items())->map(fn (JobPosting $job) => $this->formatJob($job))->values(),
            'meta' => [
                'current_page' => $jobs->currentPage(),
                'last_page' => $jobs->lastPage(),
                'total' => $jobs->total(),
            ],
        ]);
    }

    public function myInterviews(): JsonResponse
    {
        $user = Auth::user();
        abort_unless($user instanceof User && $this->nav->canViewAssignedInterviews($user), 403);

        $interviews = Interview::query()
            ->with(['applicant.jobPosting:id,title'])
            ->where('interviewer_id', $user->id)
            ->orderBy('scheduled_at')
            ->get();

        return response()->json([
            'data' => $interviews->map(fn (Interview $interview) => [
                'id' => $interview->id,
                'applicant_name' => $interview->applicant?->name,
                'job_title' => $interview->applicant?->jobPosting?->title,
                'interview_type' => $interview->interview_type,
                'meeting_link' => $interview->meeting_link,
                'resume_url' => $interview->applicant?->resume_path
                    ? route('applicants.resume.download', $interview->applicant)
                    : null,
                'scheduled_at' => $interview->scheduled_at?->toIso8601String(),
                'status' => $interview->status,
            ])->values(),
        ]);
    }

    public function myReferrals(Request $request): JsonResponse
    {
        $user = Auth::user();
        abort_unless($user instanceof User && $this->scope->canViewOwnReferrals($user), 403);

        $status = (string) $request->query('status', 'active');
        $query = $this->scope->scopedQuery($user)
            ->with('jobPosting:id,title')
            ->whereNotNull('referred_by_user_id')
            ->when($status === 'active', fn ($q) => $q->whereNotIn('stage', ['rejected', 'offer']))
            ->when($status === 'closed', fn ($q) => $q->whereIn('stage', ['rejected', 'offer']))
            ->orderByDesc('created_at');

        $page = $query->paginate(min(50, (int) $request->query('per_page', 15)));

        return response()->json([
            'data' => collect($page->items())->map(fn (Applicant $a) => $this->formatReferral($a))->values(),
            'meta' => [
                'current_page' => $page->currentPage(),
                'last_page' => $page->lastPage(),
                'total' => $page->total(),
            ],
        ]);
    }

    public function showReferral(Applicant $applicant): JsonResponse
    {
        $user = Auth::user();
        abort_unless($user instanceof User, 403);
        abort_unless((int) $applicant->referred_by_user_id === (int) $user->id, 403);

        $applicant->load(['jobPosting:id,title', 'stageEvents' => fn ($q) => $q->orderBy('created_at')]);

        return response()->json($this->formatReferralDetail($applicant));
    }

    public function storeRefer(Request $request): JsonResponse
    {
        $user = Auth::user();
        abort_unless($user instanceof User && $this->nav->canRefer($user), 403);

        $companyId = (int) $user->company_id;
        $validated = $request->validate([
            'job_posting_id' => ['required', Rule::exists('job_postings', 'id')->where('company_id', $companyId)],
            'name' => ['required', 'string', 'max:191'],
            'email' => ['nullable', 'email', 'max:191'],
            'phone' => ['nullable', 'string', 'max:50'],
            'notes' => ['nullable', 'string', 'max:2000'],
            'resume' => ['nullable', 'file', 'mimes:pdf,doc,docx', 'max:5120'],
        ]);

        if ($this->duplicates->isDuplicate($companyId, (int) $validated['job_posting_id'], $validated['email'] ?? null)) {
            return response()->json(['message' => 'This email already has an active application for this role.'], 422);
        }

        $resumePath = $request->hasFile('resume')
            ? $request->file('resume')->store('resumes/'.$companyId, $this->privateUploadDisk())
            : null;

        $appliedAt = now();
        $applicant = Applicant::create([
            'company_id' => $companyId,
            'job_posting_id' => $validated['job_posting_id'],
            'referred_by_user_id' => $user->id,
            'source' => 'referral',
            'name' => $validated['name'],
            'email' => $validated['email'] ?? null,
            'phone' => $validated['phone'] ?? null,
            'stage' => 'applied',
            'applied_at' => $appliedAt->toDateString(),
            'sla_due_at' => $this->sla->dueAtForNewApplicant($appliedAt),
            'notes' => $validated['notes'] ?? null,
            'resume_path' => $resumePath,
        ]);

        $this->stageService->recordInitial($applicant, $user);

        return response()->json($this->formatReferralDetail($applicant->fresh(['jobPosting', 'stageEvents'])), 201);
    }

    public function pipeline(Request $request): JsonResponse
    {
        $user = Auth::user();
        abort_unless($user instanceof User && $this->nav->hasHrAccess($user), 403);

        $groups = $this->home->pipelineBoardGroups();

        return response()->json([
            'columns' => collect($groups)->map(fn ($items, $stage) => [
                'stage' => $stage,
                'count' => $items->count(),
                'applicants' => $items->map(fn (Applicant $a) => [
                    'id' => $a->id,
                    'name' => $a->name,
                    'source' => $a->source,
                    'job_title' => $a->jobPosting?->title,
                    'referrer' => $a->referredBy?->name,
                    'sla_overdue' => $this->sla->isOverdue($a),
                ])->values(),
            ])->values(),
        ]);
    }

    public function referralLeaderboard(): JsonResponse
    {
        $user = Auth::user();
        abort_unless($user instanceof User && $this->nav->hasHrAccess($user), 403);

        $now = now();
        $leaderboard = $this->home->referralLeaderboard($now->copy()->startOfMonth(), $now->copy()->endOfDay());

        return response()->json(['data' => $leaderboard]);
    }

    protected function formatJob(JobPosting $job): array
    {
        return [
            'id' => $job->id,
            'title' => $job->title,
            'department' => $job->department?->name,
            'location' => $job->location,
            'employment_type' => $job->employment_type,
            'openings' => $job->openings,
            'posted_at' => $job->posted_at?->format('Y-m-d'),
            'description' => $job->description,
        ];
    }

    protected function formatReferral(Applicant $applicant): array
    {
        $progress = $this->progress->build($applicant);

        return [
            'id' => $applicant->id,
            'name' => $applicant->name,
            'job_title' => $applicant->jobPosting?->title,
            'stage' => $applicant->stage,
            'staff_label' => $progress['staff_label'],
            'percent' => $progress['percent'],
            'applied_at' => $applicant->applied_at?->format('Y-m-d'),
        ];
    }

    protected function formatReferralDetail(Applicant $applicant): array
    {
        $progress = $this->progress->build($applicant);

        return array_merge($this->formatReferral($applicant), [
            'email' => $applicant->email,
            'phone' => $applicant->phone,
            'progress' => $progress,
            'timeline' => $applicant->stageEvents->map(fn ($event) => [
                'to_stage' => $event->to_stage,
                'label' => $this->progress->staffLabel($event->to_stage),
                'note' => $event->note_referrer,
                'at' => $event->created_at?->toIso8601String(),
            ])->values(),
            'has_resume' => filled($applicant->resume_path) && $this->privateUploadExists($applicant->resume_path),
        ]);
    }
}
