<?php

namespace App\Services\Member;

use App\Models\FreelanceBrief;
use App\Models\FreelanceMatch;
use App\Models\FreelanceProfile;
use App\Models\Hub;
use App\Models\Review;
use App\Models\User;
use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\DB;

class FreelanceService
{
    // ── Landing Page ───────────────────────────────────────────

    /**
     * Data for the Help & Earn landing page.
     * Returns: active profile pool + open briefs + current user state
     */
    public function index(User $user, array $filters = []): array
    {
        $hubs = $this->hubs();

        // Profile pool — paginated, 12 per page
        $profileQuery = FreelanceProfile::with(['user', 'hub'])
            ->active()
            ->when($filters['hub_id'] ?? null, fn ($q, $id) => $q->forHub($id))
            ->when($filters['search'] ?? null, fn ($q, $s) =>
                $q->where('headline', 'like', "%{$s}%")
                  ->orWhere('description', 'like', "%{$s}%")
            )
            ->when($filters['availability'] ?? null, fn ($q, $a) =>
                $q->where('availability', $a)
            )
            ->orderByDesc('rating_average')
            ->orderByDesc('review_count');

        $profiles = $profileQuery->paginate(12)->withQueryString();

        // Open briefs — paginated, 10 per page
        $briefQuery = FreelanceBrief::with(['user', 'hub'])
            ->open()
            ->when($filters['hub_id'] ?? null, fn ($q, $id) => $q->forHub($id))
            ->when($filters['brief_search'] ?? null, fn ($q, $s) =>
                $q->where('title', 'like', "%{$s}%")
                  ->orWhere('description', 'like', "%{$s}%")
            )
            ->orderByDesc('created_at');

        $briefs = $briefQuery->paginate(10)->withQueryString();

        // Current user state
        $myProfile    = FreelanceProfile::where('user_id', $user->id)->first();
        $myBriefs     = FreelanceBrief::where('user_id', $user->id)
                            ->orderByDesc('created_at')
                            ->limit(5)
                            ->get();
        $myMatches    = FreelanceMatch::with(['brief', 'profile'])
                            ->where(fn ($q) =>
                                $q->where('seeker_user_id', $user->id)
                                  ->orWhere('provider_user_id', $user->id)
                            )
                            ->whereIn('status', ['pending', 'interested'])
                            ->count();

        return compact('hubs', 'profiles', 'briefs', 'myProfile', 'myBriefs', 'myMatches', 'filters');
    }

    // ── Freelance Profile ──────────────────────────────────────

    /**
     * Data for the create/edit profile form.
     */
    public function profileForm(User $user): array
    {
        $hubs      = $this->hubs();
        $myProfile = FreelanceProfile::where('user_id', $user->id)->first();

        return compact('hubs', 'myProfile');
    }

    /**
     * Create a new freelance profile.
     * One profile per user — enforced here.
     */
    public function storeProfile(User $user, array $data): FreelanceProfile
    {
        return DB::transaction(function () use ($user, $data) {
            // One profile per user
            $existing = FreelanceProfile::where('user_id', $user->id)->first();
            if ($existing) {
                return $this->updateProfile($existing, $data);
            }

            $profile = FreelanceProfile::create([
                'user_id'          => $user->id,
                'hub_id'           => $data['hub_id'],
                'headline'         => $data['headline'],
                'description'      => $data['description'],
                'services'         => $data['services'],
                'sector_expertise' => $data['sector_expertise'] ?? null,
                'experience_level' => $data['experience_level'],
                'availability'     => $data['availability'],
                'rate_min_egp'     => $data['rate_min_egp'] ?? null,
                'rate_max_egp'     => $data['rate_max_egp'] ?? null,
                'rate_is_visible'  => $data['rate_is_visible'] ?? true,
                'linkedin_url'     => $data['linkedin_url'] ?? null,
                'is_active'        => true,
            ]);

            Cache::forget("hub_stats_{$data['hub_id']}");

            return $profile;
        });
    }

    /**
     * Update an existing freelance profile.
     */
    public function updateProfile(FreelanceProfile $profile, array $data): FreelanceProfile
    {
        return DB::transaction(function () use ($profile, $data) {
            $profile->update([
                'hub_id'           => $data['hub_id'],
                'headline'         => $data['headline'],
                'description'      => $data['description'],
                'services'         => $data['services'],
                'sector_expertise' => $data['sector_expertise'] ?? null,
                'experience_level' => $data['experience_level'],
                'availability'     => $data['availability'],
                'rate_min_egp'     => $data['rate_min_egp'] ?? null,
                'rate_max_egp'     => $data['rate_max_egp'] ?? null,
                'rate_is_visible'  => $data['rate_is_visible'] ?? true,
                'linkedin_url'     => $data['linkedin_url'] ?? null,
            ]);

            Cache::forget("hub_stats_{$profile->hub_id}");
            Cache::forget("hub_stats_{$data['hub_id']}");

            return $profile->fresh();
        });
    }

    /**
     * Toggle profile active/inactive.
     */
    public function toggleProfile(FreelanceProfile $profile): FreelanceProfile
    {
        $profile->update(['is_active' => ! $profile->is_active]);
        return $profile->fresh();
    }

    /**
     * Public profile page — with reviews and match state.
     */
    public function showProfile(FreelanceProfile $profile, User $viewer): array
    {
        $profile->load(['user', 'hub', 'reviews.reviewer']);

        // Has this viewer already sent interest to this profile?
        $existingMatch = FreelanceMatch::where('seeker_user_id', $viewer->id)
            ->where('freelance_profile_id', $profile->id)
            ->whereNotIn('status', ['declined'])
            ->first();

        // Is the viewer the owner?
        $isOwner = $profile->user_id === $viewer->id;

        // Format reviews
        $reviews = $profile->reviews()
            ->with('reviewer')
            ->orderByDesc('created_at')
            ->get()
            ->map(fn ($r) => [
                'id'         => $r->id,
                'rating'     => $r->rating,
                'comment'    => $r->comment,
                'author'     => $r->author_name,
                'created_at' => $r->created_at->diffForHumans(),
            ]);

        return compact('profile', 'reviews', 'existingMatch', 'isOwner');
    }

    // ── Freelance Briefs ───────────────────────────────────────

    /**
     * Data for the create brief form.
     */
    public function briefForm(): array
    {
        return ['hubs' => $this->hubs()];
    }

    /**
     * Create a new brief.
     */
    public function storeBrief(User $user, array $data): FreelanceBrief
    {
        return DB::transaction(function () use ($user, $data) {
            return FreelanceBrief::create([
                'user_id'           => $user->id,
                'hub_id'            => $data['hub_id'],
                'title'             => $data['title'],
                'description'       => $data['description'],
                'required_skills'   => $data['required_skills'] ?? null,
                'preferred_sector'  => $data['preferred_sector'] ?? null,
                'duration_estimate' => $data['duration_estimate'] ?? null,
                'timeline'          => $data['timeline'] ?? null,
                'budget_min_egp'    => $data['budget_min_egp'] ?? null,
                'budget_max_egp'    => $data['budget_max_egp'] ?? null,
                'budget_is_visible' => $data['budget_is_visible'] ?? true,
                'status'            => 'open',
            ]);
        });
    }

    /**
     * Show a single brief — with matched profiles if any.
     */
    public function showBrief(FreelanceBrief $brief, User $viewer): array
    {
        $brief->load(['user', 'hub']);

        $isOwner = $brief->user_id === $viewer->id;

        // If owner — show all matches for this brief
        $matches = $isOwner
            ? FreelanceMatch::with(['profile.user'])
                ->where('freelance_brief_id', $brief->id)
                ->orderByDesc('created_at')
                ->get()
                ->map(fn ($m) => [
                    'id'              => $m->id,
                    'status'          => $m->status,
                    'seeker_consented'   => $m->seeker_consented,
                    'provider_consented' => $m->provider_consented,
                    'profile' => [
                        'id'           => $m->profile->id,
                        'headline'     => $m->profile->headline,
                        'experience'   => $m->profile->experience_level,
                        'availability' => $m->profile->availability,
                        'rating'       => $m->profile->rating_average,
                        'reviews'      => $m->profile->review_count,
                        'linkedin_url' => $m->profile->linkedin_url,
                    ],
                ])
            : collect();

        return compact('brief', 'isOwner', 'matches');
    }

    /**
     * Close a brief — only the owner can do this.
     */
    public function closeBrief(FreelanceBrief $brief): void
    {
        $brief->update(['status' => 'closed']);
    }

    // ── Matching & Consent Flow ────────────────────────────────

    /**
     * Seeker sends interest to a freelancer profile.
     * Creates the match record with status = pending.
     */
    public function sendInterest(User $seeker, FreelanceProfile $profile, string $message): FreelanceMatch
    {
        return DB::transaction(function () use ($seeker, $profile, $message) {
            // Prevent duplicate — one active match per pair
            $existing = FreelanceMatch::where('seeker_user_id', $seeker->id)
                ->where('freelance_profile_id', $profile->id)
                ->whereNotIn('status', ['declined'])
                ->first();

            if ($existing) {
                return $existing;
            }

            return FreelanceMatch::create([
                'freelance_profile_id' => $profile->id,
                'freelance_brief_id'   => null, // direct interest — no brief
                'seeker_user_id'       => $seeker->id,
                'provider_user_id'     => $profile->user_id,
                'status'               => 'pending',
                'seeker_consented'     => false,
                'provider_consented'   => false,
                'seeker_message'       => $message,
            ]);
        });
    }

    /**
     * Freelancer responds to a pending match.
     * status = interested | declined
     */
    public function respond(FreelanceMatch $match, string $response): FreelanceMatch
    {
        abort_if(
            ! in_array($response, ['interested', 'declined']),
            422,
            'Invalid response'
        );

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

        return $match->fresh();
    }

    /**
     * One side confirms consent.
     * When BOTH have consented → status becomes connected.
     */
    public function consent(FreelanceMatch $match, User $user): FreelanceMatch
    {
        return DB::transaction(function () use ($match, $user) {
            if ($user->id === $match->seeker_user_id) {
                $match->update(['seeker_consented' => true]);
            } elseif ($user->id === $match->provider_user_id) {
                $match->update(['provider_consented' => true]);
            }

            $match->refresh();

            if ($match->bothConsented()) {
                $match->update([
                    'status'       => 'connected',
                    'connected_at' => now(),
                ]);
            }
        
            $updated = $match->fresh();

            if ($updated->isConnected()) {
                $other = $user->id === $updated->seeker_user_id
                    ? $updated->provider
                    : $updated->seeker;

                $updated->contact_info = [
                    'name'  => $other->name,
                    'email' => $other->email,
                    'phone' => $other->phone,
                ];
            }

            return $updated;
        });
    }

    /**
     * My matches inbox — everything related to this user.
     */
    public function myMatches(User $user): array
    {
        $asProvider = FreelanceMatch::with(['brief', 'seeker'])
            ->where('provider_user_id', $user->id)
            ->orderByDesc('updated_at')
            ->get();

        $asSeeker = FreelanceMatch::with(['profile.user', 'brief'])
            ->where('seeker_user_id', $user->id)
            ->orderByDesc('updated_at')
            ->get();

        return compact('asProvider', 'asSeeker');
    }

    // ── Reviews ────────────────────────────────────────────────

    /**
     * Post a review after a connected engagement.
     * Each reviewer can only review once per match.
     */
    public function storeReview(FreelanceMatch $match, User $reviewer, array $data): Review
    {
        abort_unless($match->isConnected(), 403, 'Can only review after connection');

        // Reviewer must be part of this match
        abort_unless(
            in_array($reviewer->id, [$match->seeker_user_id, $match->provider_user_id]),
            403
        );

        // Only the seeker reviews the freelancer (provider)
        $profileId = $match->freelance_profile_id;

        return DB::transaction(function () use ($match, $reviewer, $data, $profileId) {
            $review = Review::create([
                'freelance_match_id'   => $match->id,
                'reviewer_user_id'     => $reviewer->id,
                'freelance_profile_id' => $profileId,
                'rating'               => $data['rating'],
                'comment'              => $data['comment'] ?? null,
                'posted_as_nickname'   => $data['posted_as_nickname'] ?? true,
            ]);

            // Update cached aggregates on the profile
            $profile = FreelanceProfile::find($profileId);
            $stats = Review::where('freelance_profile_id', $profileId)
                ->selectRaw('AVG(rating) as avg, COUNT(*) as cnt')
                ->first();
            $avg   = $stats->avg ?? 0;
            $count = $stats->cnt ?? 0;

            $profile->update([
                'rating_average' => round($avg, 2),
                'review_count'   => $count,
            ]);

            $review->load('reviewer');
            return $review;
        });
    }

    // ── Helpers ────────────────────────────────────────────────

    private function hubs(): \Illuminate\Database\Eloquent\Collection
    {
        return Cache::remember('hubs.active', 3600, fn () =>
            Hub::active()->ordered()->get()
        );
    }
}