<?php

namespace App\Services\Member;

use App\Models\CvProfile;
use App\Models\Hub;
use App\Models\JobListing;
use App\Models\JobNotification;
use App\Models\User;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\DB;

// ══════════════════════════════════════════════════════════════════
//  InPractice — JobService
//  Location: app/Services/Member/JobService.php
//
//  All business logic for the Job Listings feature.
//
//  Methods:
//    index()          → browse active listings with filters + pagination
//    show()           → single listing with applicants (consent-aware)
//    myListings()     → listings posted by the authenticated user
//    store()          → create a new job listing
//    close()          → mark a listing as filled/closed
//    apply()          → member applies to a public listing (status: pending)
//    sendDirect()     → member sends job directly to a CV profile
//    respond()        → professional accepts or declines interest
//    consent()        → one side confirms contact sharing
//    notifications()  → professional's inbox of incoming interest
//
//  Consent flow (matches the agreed design):
//    Public listing:
//      Member applies → status: pending
//      Poster reviews applicants → clicks "I want to contact"
//        → consent gate fires for poster (employer_consented = true)
//        → professional receives notification → consent gate fires
//        → professional confirms (professional_consented = true)
//        → status: connected → contact details visible to both
//
//    Direct send:
//      Poster sends to specific CV profile → status: pending
//      Professional sees notification → Accept or Decline
//      If Accept → status: accepted → consent gate fires for professional
//        → professional_consented = true
//        → poster receives notification → consent gate fires for poster
//        → employer_consented = true
//        → status: connected
//
//  N+1 rules:
//    - index(): one paginated query, hub via eager load
//    - show() applicants: one query with cvProfile (no personal info)
//      personal info added only when status = connected
//    - notifications(): one query with jobListing.hub + jobListing eager
// ══════════════════════════════════════════════════════════════════

class JobService
{
    // ── Browse Listings ───────────────────────────────────────

    public function index(User $viewer, array $filters = []): array
    {
        $query = JobListing::active()
            ->with(['hub:id,name_en,name_ar,color,icon'])
            ->withCount([
                // Count pending + accepted applicants (not declined, not connected yet)
                'notifications as applicant_count' => function ($q) {
                    $q->whereIn('status', ['pending', 'accepted', 'connected']);
                },
            ])
            ->select([
                'id', 'hub_id', 'posted_by', 'job_title', 'company_name',
                'industry', 'location_city', 'job_type', 'description',
                'required_skills', 'min_experience_years', 'salary_range',
                'status', 'created_at',
            ]);

        // ── Filters ────────────────────────────────────────────
        if (! empty($filters['hub_id'])) {
            $query->where('hub_id', (int) $filters['hub_id']);
        }

        if (! empty($filters['job_type'])) {
            $query->where('job_type', $filters['job_type']);
        }

        if (! empty($filters['search'])) {
            $query->where(function ($q) use ($filters) {
                $q->where('job_title', 'like', '%' . $filters['search'] . '%')
                  ->orWhere('company_name', 'like', '%' . $filters['search'] . '%')
                  ->orWhere('industry', 'like', '%' . $filters['search'] . '%');
            });
        }

        $listings = $query->latest()->paginate(12)->withQueryString();

        // For each listing, check if the viewer has already applied
        $listingIds = $listings->pluck('id')->toArray();
        $myApplications = JobNotification::where('professional_user_id', $viewer->id)
            ->whereIn('job_listing_id', $listingIds)
            ->pluck('status', 'job_listing_id');

        $listings->getCollection()->transform(function ($listing) use ($viewer, $myApplications) {
            return $this->formatListing($listing, $viewer, $myApplications);
        });

        return [
            'listings'   => $listings,
            'allHubs'    => $this->getHubs(),
            'filters'    => $filters,
        ];
    }

    // ── Single Listing ────────────────────────────────────────

    public function show(User $viewer, JobListing $listing): array
    {
        $listing->load(['hub:id,name_en,name_ar,color,icon', 'employer:id,nickname']);

        // Has the viewer already applied?
        $myApplication = JobNotification::where('professional_user_id', $viewer->id)
            ->where('job_listing_id', $listing->id)
            ->first();

        // Is the viewer the poster?
        $isPoster = $listing->posted_by === $viewer->id;

        // If poster: load applicants — one query, consent-aware
        $applicants = [];
        if ($isPoster) {
            $notifications = JobNotification::where('job_listing_id', $listing->id)
                ->whereIn('status', ['pending', 'accepted', 'connected'])
                ->with(['cvProfile:id,job_title,years_experience,experience_level,skills,summary,location_city'])
                ->get();

            $applicants = $notifications->map(function ($notif) {
                return $this->formatApplicant($notif);
            })->values()->toArray();
        }

        return [
            'listing'       => $this->formatListingFull($listing),
            'isPoster'      => $isPoster,
            'myApplication' => $myApplication ? [
                'id'     => $myApplication->id,
                'status' => $myApplication->status,
            ] : null,
            'applicants'    => $applicants,
            'allHubs'       => $this->getHubs(),
        ];
    }

    // ── My Listings ───────────────────────────────────────────

    public function myListings(User $user): array
    {
        $listings = JobListing::where('posted_by', $user->id)
            ->with(['hub:id,name_en,name_ar,color'])
            ->withCount([
                'notifications as applicant_count' => function ($q) {
                    $q->whereIn('status', ['pending', 'accepted', 'connected']);
                },
            ])
            ->latest()
            ->get()
            ->map(fn ($l) => $this->formatListingCard($l));

        return [
            'myListings' => $listings,
            'allHubs'    => $this->getHubs(),
        ];
    }

    // ── Store Job Listing ─────────────────────────────────────

    public function store(User $user, array $data): JobListing
    {
        return DB::transaction(function () use ($user, $data) {
            return JobListing::create(array_merge($data, [
                'posted_by' => $user->id,
                'status'    => 'active',
            ]));
        });
    }

    // ── Close / Reopen Listing ────────────────────────────────

    public function close(User $user, JobListing $listing): void
    {
        abort_if($listing->posted_by !== $user->id, 403);

        $listing->update(['status' => 'filled']);
    }

    public function reopen(User $user, JobListing $listing): void
    {
        abort_if($listing->posted_by !== $user->id, 403);

        $listing->update(['status' => 'active']);
    }

    // ── Apply to Public Listing ───────────────────────────────
    // Member clicks "I'm Interested" on a public job listing.
    // Their CV becomes visible to the poster (pending status).
    // Idempotent — cannot apply twice.

    public function apply(User $applicant, JobListing $listing, ?string $message = null): JobNotification
    {
        // Cannot apply to own listing
        abort_if($listing->posted_by === $applicant->id, 403);

        // Cannot apply to a closed listing
        abort_if($listing->status !== 'active', 422);

        // Must have a CV profile
        $cvProfile = CvProfile::where('user_id', $applicant->id)->firstOrFail();

        // Idempotent — return existing if already applied
        $existing = JobNotification::where('job_listing_id', $listing->id)
            ->where('professional_user_id', $applicant->id)
            ->first();

        if ($existing) {
            return $existing;
        }

        return DB::transaction(function () use ($applicant, $listing, $cvProfile, $message) {
            return JobNotification::create([
                'job_listing_id'       => $listing->id,
                'cv_profile_id'        => $cvProfile->id,
                'professional_user_id' => $applicant->id,
                'employer_user_id'     => $listing->posted_by,
                'message'              => $message,
                'status'               => 'pending',
            ]);
        });
    }

    // ── Send Direct Interest (from CV pool) ───────────────────
    // Poster finds a CV in the pool and sends them a specific job.
    // Idempotent — cannot send the same job to the same CV twice.

    public function sendDirect(
        User $poster,
        JobListing $listing,
        CvProfile $cvProfile,
        ?string $message = null
    ): JobNotification {
        // Must own the listing
        abort_if($listing->posted_by !== $poster->id, 403);

        // Cannot send to yourself
        abort_if($cvProfile->user_id === $poster->id, 403);

        // Cannot send for a closed listing
        abort_if($listing->status !== 'active', 422);

        // Idempotent
        $existing = JobNotification::where('job_listing_id', $listing->id)
            ->where('cv_profile_id', $cvProfile->id)
            ->first();

        if ($existing) {
            return $existing;
        }

        return DB::transaction(function () use ($poster, $listing, $cvProfile, $message) {
            return JobNotification::create([
                'job_listing_id'       => $listing->id,
                'cv_profile_id'        => $cvProfile->id,
                'professional_user_id' => $cvProfile->user_id,
                'employer_user_id'     => $poster->id,
                'message'              => $message,
                'status'               => 'pending',
            ]);
        });
    }

    // ── Professional Responds ─────────────────────────────────
    // Professional accepts or declines an interest notification.
    // Only valid from pending status.

    public function respond(User $professional, JobNotification $notification, string $response): JobNotification
    {
        // Must be the professional
        abort_if($notification->professional_user_id !== $professional->id, 403);

        // Can only respond to pending
        abort_if($notification->status !== 'pending', 422);

        abort_if(! in_array($response, ['accepted', 'declined']), 422);

        $notification->update(['status' => $response]);

        return $notification->fresh();
    }

    // ── Consent ───────────────────────────────────────────────
    // Either side confirms contact sharing.
    // Once both consent: status → connected, connected_at recorded.
    // After this point both sides can see each other's contact info.

    public function consent(User $user, JobNotification $notification): array
    {
        // Must be accepted before consent is possible
        abort_if($notification->status !== 'accepted', 422);

        DB::transaction(function () use ($user, $notification) {
            if ($notification->professional_user_id === $user->id) {
                $notification->update(['professional_consented' => true]);
            } elseif ($notification->employer_user_id === $user->id) {
                $notification->update(['employer_consented' => true]);
            } else {
                abort(403);
            }

            // Check if both have consented now
            $notification->refresh();

            if ($notification->professional_consented && $notification->employer_consented) {
                $notification->update([
                    'status'       => 'connected',
                    'connected_at' => now(),
                ]);
            }
        });

        $notification->refresh();

        return [
            'status'                => $notification->status,
            'professional_consented'=> $notification->professional_consented,
            'employer_consented'    => $notification->employer_consented,
            'connected_at'          => $notification->connected_at?->toDateTimeString(),
        ];
    }

    // ── Professional Notifications Inbox ──────────────────────
    // The professional sees all incoming interest requests.

    public function notifications(User $professional): array
    {
        // One query — eager load job listing and hub
        // No N+1 — hub is on job listing, loaded in one join
        $notifications = JobNotification::where('professional_user_id', $professional->id)
            ->with([
                'jobListing:id,job_title,company_name,job_type,hub_id,posted_by,status',
                'jobListing.hub:id,name_en,name_ar,color',
            ])
            ->latest()
            ->paginate(10)
            ->withQueryString();

        $notifications->getCollection()->transform(function ($notif) use ($professional) {
            return $this->formatNotification($notif, $professional);
        });

        return [
            'notifications' => $notifications,
        ];
    }

    // ── Poster Reviews Applicants ─────────────────────────────
    // Poster wants to contact a specific applicant — triggers consent flow.
    // This is the "I want to contact this person" action from the listing.

    public function initiateContact(User $poster, JobNotification $notification): array
    {
        // Must own the listing
        abort_if($notification->employer_user_id !== $poster->id, 403);

        // Must be in pending status (applicant applied, poster has not yet responded)
        abort_if($notification->status !== 'pending', 422);

        // Move to accepted so the consent flow can start
        $notification->update([
            'status'              => 'accepted',
            'employer_consented'  => true,  // poster is initiating — they consent first
        ]);

        return [
            'status'              => 'accepted',
            'employer_consented'  => true,
        ];
    }

    // ── Private helpers ───────────────────────────────────────

    private function formatListing(JobListing $listing, User $viewer, $myApplications): array
    {
        return [
            'id'                   => $listing->id,
            'job_title'            => $listing->job_title,
            'company_name'         => $listing->company_name,
            'industry'             => $listing->industry,
            'location_city'        => $listing->location_city,
            'job_type'             => $listing->job_type,
            'required_skills'      => $listing->required_skills,
            'min_experience_years' => $listing->min_experience_years,
            'salary_range'         => $listing->salary_range,
            'applicant_count'      => $listing->applicant_count ?? 0,
            'is_mine'              => $listing->posted_by === $viewer->id,
            'my_status'            => $myApplications[$listing->id] ?? null,
            'created_at'           => $listing->created_at->diffForHumans(),
            'hub' => [
                'name_en' => $listing->hub->name_en,
                'name_ar' => $listing->hub->name_ar,
                'color'   => $listing->hub->color,
                'icon'    => $listing->hub->icon,
            ],
        ];
    }

    private function formatListingFull(JobListing $listing): array
    {
        return array_merge($this->formatListingCard($listing), [
            'description'  => $listing->description,
            'posted_by_nickname' => $listing->employer->nickname ?? null,
        ]);
    }

    private function formatListingCard(JobListing $listing): array
    {
        return [
            'id'                   => $listing->id,
            'job_title'            => $listing->job_title,
            'company_name'         => $listing->company_name,
            'industry'             => $listing->industry,
            'location_city'        => $listing->location_city,
            'job_type'             => $listing->job_type,
            'required_skills'      => $listing->required_skills,
            'min_experience_years' => $listing->min_experience_years,
            'salary_range'         => $listing->salary_range,
            'status'               => $listing->status,
            'applicant_count'      => $listing->applicant_count ?? 0,
            'created_at'           => $listing->created_at->diffForHumans(),
            'hub' => $listing->hub ? [
                'name_en' => $listing->hub->name_en,
                'name_ar' => $listing->hub->name_ar,
                'color'   => $listing->hub->color,
            ] : null,
        ];
    }

    /**
     * Format an applicant for the poster's view.
     * Personal info (name, email, phone) only shown when status = connected.
     */
    private function formatApplicant(JobNotification $notif): array
    {
        $isConnected = $notif->status === 'connected';

        $base = [
            'notification_id'       => $notif->id,
            'status'                => $notif->status,
            'message'               => $notif->message,
            'professional_consented'=> $notif->professional_consented,
            'employer_consented'    => $notif->employer_consented,
            'connected_at'          => $notif->connected_at?->toDateTimeString(),
            'cv' => $notif->cvProfile ? [
                'id'               => $notif->cvProfile->id,
                'job_title'        => $notif->cvProfile->job_title,
                'years_experience' => $notif->cvProfile->years_experience,
                'experience_level' => $notif->cvProfile->experience_level,
                'skills'           => $notif->cvProfile->skills,
                'summary'          => $notif->cvProfile->summary,
                'location_city'    => $notif->cvProfile->location_city,
            ] : null,
        ];

        // Only expose personal info after full connection
        if ($isConnected) {
            $professional = $notif->professional()->select('id', 'name', 'email', 'phone')->first();
            $base['contact'] = [
                'name'  => $professional?->name,
                'email' => $professional?->email,
                'phone' => $professional?->phone,
            ];
        }

        return $base;
    }

    /**
     * Format a notification for the professional's inbox.
     * Personal info of the poster only shown when status = connected.
     */
    private function formatNotification(JobNotification $notif, User $professional): array
    {
        $isConnected = $notif->status === 'connected';

        $base = [
            'id'                    => $notif->id,
            'status'                => $notif->status,
            'message'               => $notif->message,
            'professional_consented'=> $notif->professional_consented,
            'employer_consented'    => $notif->employer_consented,
            'connected_at'          => $notif->connected_at?->toDateTimeString(),
            'created_at'            => $notif->created_at->diffForHumans(),
            'job' => $notif->jobListing ? [
                'id'           => $notif->jobListing->id,
                'job_title'    => $notif->jobListing->job_title,
                'company_name' => $notif->jobListing->company_name,
                'job_type'     => $notif->jobListing->job_type,
                'status'       => $notif->jobListing->status,
                'hub' => $notif->jobListing->hub ? [
                    'name_en' => $notif->jobListing->hub->name_en,
                    'name_ar' => $notif->jobListing->hub->name_ar,
                    'color'   => $notif->jobListing->hub->color,
                ] : null,
            ] : null,
        ];

        if ($isConnected) {
            $employer = $notif->employer()->select('id', 'name', 'email', 'phone')->first();
            $base['contact'] = [
                'name'  => $employer?->name,
                'email' => $employer?->email,
                'phone' => $employer?->phone,
            ];
        }

        return $base;
    }

    private function getHubs(): \Illuminate\Database\Eloquent\Collection
    {
        return Cache::remember('hubs.active', 3600, function () {
            return Hub::where('is_active', true)
                ->select('id', 'name_en', 'name_ar', 'color', 'icon')
                ->orderBy('sort_order')
                ->get();
        });
    }
}
