<?php

namespace App\Http\Controllers;

use App\Models\Continent;
use App\Models\Lead;
use App\Models\LeadRemark;
use App\Models\LeadView;
use App\Models\Tradefair;
use App\Models\User;
use App\Models\VendorQuote;
use App\Services\VisibilityService;
use Illuminate\Support\Facades\DB;
use Illuminate\Http\Request;
use Illuminate\Validation\Rule;

class LeadController extends Controller
{
    public function dashboard()
    {
        $leads = Lead::with(['vendorQuotes', 'tradefair'])
            ->get()
            ->sortBy(function (Lead $lead) {
                $d = $lead->effectiveFairStartDate();

                return $d ? $d->copy()->startOfDay()->timestamp : PHP_INT_MAX;
            })
            ->values();

        $today = now()->startOfDay();

        $salesLeads = $leads->filter(function (Lead $lead) {
            return in_array($lead->clean_lead_status, ['Quoted', 'Negotiation'], true);
        });

        $deadlineAlerts = $leads->filter(function (Lead $lead) use ($today) {
            $start = $lead->effectiveFairStartDate();
            if (!$start) {
                return false;
            }

            $daysToFair = $today->diffInDays($start, false);
            return $daysToFair >= 0 && $daysToFair < 21 && $lead->clean_lead_status !== 'Won/Ongoing';
        });

        $projectVendorPrices = $leads->map(function (Lead $lead) {
            $selectedQuote = $lead->vendorQuotes->firstWhere('selection_toggle', true);
            $fallbackQuote = $lead->vendorQuotes->sortBy('client_final_price')->first();
            $quoteToShow = $selectedQuote ?: $fallbackQuote;

            if (!$quoteToShow) {
                return null;
            }

            return [
                'lead' => $lead,
                'quote' => $quoteToShow,
                'is_selected' => (bool) $selectedQuote,
            ];
        })->filter()->values();

        return view('leads.dashboard', [
            'leads' => $leads,
            'salesLeads' => $salesLeads,
            'deadlineAlerts' => $deadlineAlerts,
            'projectVendorPrices' => $projectVendorPrices,
            'stats' => [
                'total_leads' => $leads->count(),
                'deadline_alerts' => $deadlineAlerts->count(),
                'sales_pipeline' => $salesLeads->count(),
                'with_quotes' => $projectVendorPrices->count(),
            ],
        ]);
    }

    public function listing()
    {
        $userId = auth()->id();

        $leads = Lead::with(['tradefair', 'assignedTo'])
            ->withCount('vendorQuotes')
            ->with(['leadViews' => function ($query) use ($userId) {
                $query->where('user_id', $userId);
            }])
            ->get()
            ->sortBy(function (Lead $lead) {
                $openedByMe = $lead->leadViews->isEmpty() ? 0 : 1;
                $d = $lead->effectiveFairStartDate();
                $ts = $d ? $d->copy()->startOfDay()->timestamp : PHP_INT_MAX;

                return [$openedByMe, $ts];
            })
            ->values();

        $marketingUsers = User::marketingTeamMembers();
        $canAssign = app(VisibilityService::class)->canAccess(auth()->user(), 'leads.assign');

        return view('leads.index', compact('leads', 'marketingUsers', 'canAssign'));
    }

    public function myTasks()
    {
        $leads = Lead::with(['tradefair', 'assignedBy', 'designAssignedTo', 'remarks.user'])
            ->where('assigned_to_user_id', auth()->id())
            ->orderByDesc('assigned_at')
            ->orderByDesc('id')
            ->get();

        $designTeamUsers = User::designTeamMembers();
        $canAssignDesign = app(VisibilityService::class)->canAccess(auth()->user(), 'leads.assign.design');

        return view('leads.my-tasks', compact('leads', 'designTeamUsers', 'canAssignDesign'));
    }

    public function designTasks()
    {
        $user = auth()->user();
        $visibility = app(VisibilityService::class);
        $viewAll = $user->isPlatformAdmin()
            || $visibility->canAccess($user, 'leads.listing')
            || $visibility->canAccess($user, 'leads.dashboard');

        $query = Lead::with(['tradefair', 'designAssignedTo', 'designAssignedBy', 'assignedTo', 'designRemarks.user'])
            ->whereNotNull('design_assigned_to_user_id');

        if (!$viewAll) {
            $query->where('design_assigned_to_user_id', $user->id);
        }

        $leads = $query
            ->orderByRaw('CASE WHEN design_due_date IS NULL THEN 1 ELSE 0 END')
            ->orderBy('design_due_date')
            ->orderByDesc('design_assigned_at')
            ->get();

        $stats = [
            'total' => $leads->count(),
            'pending' => $leads->where('design_task_status', 'Pending')->count(),
            'in_progress' => $leads->where('design_task_status', 'In Progress')->count(),
            'completed' => $leads->where('design_task_status', 'Completed')->count(),
            'overdue' => $leads->filter(fn (Lead $l) => $l->isDesignOverdue())->count(),
        ];

        return view('leads.design-tasks', [
            'leads' => $leads,
            'viewAll' => $viewAll,
            'stats' => $stats,
            'designStatuses' => Lead::DESIGN_TASK_STATUSES,
        ]);
    }

    public function updateDesignTask(Request $request, Lead $lead)
    {
        $this->authorizeDesignTaskManage($lead);

        $validated = $request->validate([
            'design_task_status' => 'required|in:' . implode(',', Lead::DESIGN_TASK_STATUSES),
            'design_due_date' => 'nullable|date',
            'design_remark' => 'nullable|string|max:5000',
        ]);

        $lead->design_task_status = $validated['design_task_status'];
        $lead->design_due_date = $validated['design_due_date'] ?? null;
        $lead->save();

        if (!empty(trim($validated['design_remark'] ?? ''))) {
            LeadRemark::create([
                'lead_id' => $lead->id,
                'user_id' => auth()->id(),
                'remark' => trim($validated['design_remark']),
                'type' => LeadRemark::TYPE_DESIGN,
            ]);
        }

        return redirect()->back()->with('success', 'Design task updated.');
    }

    public function assignToDesign(Request $request, Lead $lead)
    {
        if (!app(VisibilityService::class)->canAccess(auth()->user(), 'leads.assign.design')) {
            abort(403);
        }

        if (!$lead->isAssignedTo(auth()->user()) && !auth()->user()->isPlatformAdmin()) {
            return redirect()->back()->withErrors(['design' => 'You can only assign design tasks for leads assigned to you.']);
        }

        $validated = $request->validate([
            'design_assigned_to_user_id' => 'required|exists:users,id',
            'design_due_date' => 'nullable|date',
            'design_remark' => 'nullable|string|max:5000',
        ]);

        $assignee = User::with('department')->findOrFail($validated['design_assigned_to_user_id']);

        if (!$assignee->isDesignTeam()) {
            return redirect()->back()->withErrors(['design_assigned_to_user_id' => 'Selected user must belong to a design department (3D Designer, CAD, etc.).']);
        }

        $lead->update([
            'design_assigned_to_user_id' => $assignee->id,
            'design_assigned_by_user_id' => auth()->id(),
            'design_assigned_at' => now(),
            'design_task_status' => 'Pending',
            'design_due_date' => $validated['design_due_date'] ?? null,
        ]);

        if (!empty(trim($validated['design_remark'] ?? ''))) {
            LeadRemark::create([
                'lead_id' => $lead->id,
                'user_id' => auth()->id(),
                'remark' => trim($validated['design_remark']),
                'type' => LeadRemark::TYPE_DESIGN,
            ]);
        }

        return redirect()->back()->with('success', 'Design task assigned to ' . $assignee->name . '.');
    }

    public function assignToMarketing(Request $request, Lead $lead)
    {
        if (!app(VisibilityService::class)->canAccess(auth()->user(), 'leads.assign')) {
            abort(403);
        }

        $validated = $request->validate([
            'assigned_to_user_id' => 'required|exists:users,id',
        ]);

        $assignee = User::with('department')->findOrFail($validated['assigned_to_user_id']);

        if (!$assignee->isMarketingTeam()) {
            return redirect()->back()->withErrors(['assigned_to_user_id' => 'Selected user must belong to the Marketing department.']);
        }

        $lead->update([
            'assigned_to_user_id' => $assignee->id,
            'assigned_by_user_id' => auth()->id(),
            'assigned_at' => now(),
        ]);

        return redirect()->back()->with('success', 'Lead assigned to ' . $assignee->name . ' (Marketing).');
    }

    public function create()
    {
        return view('leads.create', [
            'statuses' => Lead::STATUSES,
            'tradefairs' => Tradefair::with(['fairContinent', 'fairCountry'])->orderBy('fair_name')->get(),
            'continentsQuick' => Continent::active()->orderBy('name')->get(),
        ]);
    }

    public function store(Request $request)
    {
        $validated = $this->validateLead($request);
        $this->syncLeadLocationFromTradefair($validated);

        Lead::create($validated);

        return redirect()->route('leads.listing')->with('success', 'Lead created.');
    }

    public function edit(Lead $lead)
    {
        $this->authorizeLeadEdit($lead);

        return view('leads.edit', [
            'lead' => $lead,
            'statuses' => Lead::STATUSES,
            'tradefairs' => Tradefair::with(['fairContinent', 'fairCountry'])->orderBy('fair_name')->get(),
            'continentsQuick' => Continent::active()->orderBy('name')->get(),
            'returnToMyTasks' => $lead->isAssignedTo(auth()->user()) && !app(VisibilityService::class)->canAccess(auth()->user(), 'leads.listing'),
        ]);
    }

    public function update(Request $request, Lead $lead)
    {
        $this->authorizeLeadEdit($lead);

        $validated = $this->validateLead($request);
        $this->syncLeadLocationFromTradefair($validated);

        $lead->update($validated);

        if ($lead->isAssignedTo(auth()->user()) && !app(VisibilityService::class)->canAccess(auth()->user(), 'leads.listing')) {
            return redirect()->route('leads.show', $lead)->with('success', 'Lead updated.');
        }

        return redirect()->route('leads.listing')->with('success', 'Lead updated.');
    }

    public function destroy(Lead $lead)
    {
        $lead->delete();

        return redirect()->route('leads.listing')->with('success', 'Lead deleted.');
    }

    public function show(Lead $lead)
    {
        $this->authorizeLeadAccess($lead);
        $this->recordLeadView($lead);

        $lead->load([
            'vendorQuotes',
            'leadViews.user',
            'remarks.user',
            'designRemarks.user',
            'tradefair.fairContinent',
            'tradefair.fairCountry',
            'assignedTo',
            'assignedBy',
            'designAssignedTo',
            'designAssignedBy',
        ]);

        return view('leads.show', [
            'lead' => $lead,
            'statuses' => Lead::STATUSES,
            'isMarketingTask' => $lead->isAssignedTo(auth()->user()),
        ]);
    }

    public function updateStatus(Request $request, Lead $lead)
    {
        $this->authorizeLeadAccess($lead);

        $validated = $request->validate([
            'lead_status' => 'required|in:' . implode(',', Lead::STATUSES),
        ]);

        $lead->update($validated);

        return redirect()->back()->with('success', 'Lead status updated.');
    }

    public function storeRemark(Request $request, Lead $lead)
    {
        $this->authorizeLeadAccess($lead);

        $validated = $request->validate([
            'remark' => 'required|string|max:5000',
        ]);

        LeadRemark::create([
            'lead_id' => $lead->id,
            'user_id' => auth()->id(),
            'remark' => $validated['remark'],
            'type' => LeadRemark::TYPE_GENERAL,
        ]);

        return redirect()->back()->with('success', 'Remark added.');
    }

    public function storeVendorQuote(Request $request, Lead $lead)
    {
        $this->authorizeVendorQuotes($lead);

        $validated = $request->validate([
            'vendor_name' => 'required|string|max:255',
            'vendor_raw_cost' => 'required|numeric|min:0',
            'our_markup_percent' => 'required|numeric|min:0|max:500',
            'selection_toggle' => 'nullable|boolean',
        ]);

        DB::transaction(function () use ($validated, $lead) {
            $isSelected = (bool) ($validated['selection_toggle'] ?? false);
            if ($isSelected) {
                $lead->vendorQuotes()->update(['selection_toggle' => false]);
            }

            $lead->vendorQuotes()->create([
                'vendor_name' => $validated['vendor_name'],
                'vendor_raw_cost' => $validated['vendor_raw_cost'],
                'our_markup_percent' => $validated['our_markup_percent'],
                'selection_toggle' => $isSelected,
            ]);
        });

        return redirect()->back()->with('success', 'Vendor quote added.');
    }

    public function updateVendorQuote(Request $request, Lead $lead, VendorQuote $vendorQuote)
    {
        abort_if($vendorQuote->lead_id !== $lead->id, 404);
        $this->authorizeVendorQuotes($lead);

        $validated = $request->validate([
            'vendor_name' => 'required|string|max:255',
            'vendor_raw_cost' => 'required|numeric|min:0',
            'our_markup_percent' => 'required|numeric|min:0|max:500',
            'selection_toggle' => 'nullable|boolean',
        ]);

        DB::transaction(function () use ($validated, $lead, $vendorQuote) {
            $isSelected = (bool) ($validated['selection_toggle'] ?? false);
            if ($isSelected) {
                $lead->vendorQuotes()->where('id', '!=', $vendorQuote->id)->update(['selection_toggle' => false]);
            }

            $vendorQuote->update([
                'vendor_name' => $validated['vendor_name'],
                'vendor_raw_cost' => $validated['vendor_raw_cost'],
                'our_markup_percent' => $validated['our_markup_percent'],
                'selection_toggle' => $isSelected,
            ]);
        });

        return redirect()->back()->with('success', 'Vendor quote updated.');
    }

    private function validateLead(Request $request): array
    {
        $validated = $request->validate([
            'tradefair_id' => [
                'required',
                Rule::exists('tradefairs', 'id')->where(function ($query) {
                    $query->whereNull('deleted_at');
                }),
            ],
            'source_location' => 'nullable|string|max:255',
            'contact_person_name' => 'nullable|string|max:255',
            'booth_area_sqm' => 'nullable|numeric|min:0',
            'booth_area_sqft' => 'nullable|numeric|min:0',
            'lead_budget' => 'nullable|numeric|min:0',
            'company_name' => 'nullable|string|max:255',
            'email_id' => 'required|email|max:255',
            'phone_number' => 'required|string|max:50',
            'description' => 'nullable|string',
            'lead_status' => 'nullable|in:' . implode(',', Lead::STATUSES),
        ]);

        $validated['lead_status'] = $validated['lead_status'] ?? 'New Lead';

        if (!empty($validated['booth_area_sqm']) && empty($validated['booth_area_sqft'])) {
            $validated['booth_area_sqft'] = round((float) $validated['booth_area_sqm'] * 10.7639, 2);
        }

        if (!empty($validated['booth_area_sqft']) && empty($validated['booth_area_sqm'])) {
            $validated['booth_area_sqm'] = round((float) $validated['booth_area_sqft'] / 10.7639, 2);
        }

        return $validated;
    }

    /**
     * Copy venue (city) and country from the selected trade fair onto the lead.
     *
     * @param  array<string, mixed>  $validated
     */
    private function syncLeadLocationFromTradefair(array &$validated): void
    {
        $tradefair = Tradefair::find($validated['tradefair_id']);
        if (!$tradefair) {
            return;
        }

        $validated['city'] = $tradefair->eventvenue;
        $validated['country'] = $tradefair->country;
    }

    private function recordLeadView(Lead $lead): void
    {
        $user = auth()->user();

        if (!$user) {
            return;
        }

        $view = LeadView::firstOrNew([
            'lead_id' => $lead->id,
            'user_id' => $user->id,
        ]);

        $view->view_count = ((int) $view->view_count) + 1;
        $view->last_viewed_at = now();
        $view->save();
    }

    /**
     * Full listing access, platform admin, or marketing user viewing their assigned lead.
     */
    private function authorizeLeadAccess(Lead $lead): void
    {
        $user = auth()->user();
        $visibility = app(VisibilityService::class);

        if (!$user) {
            abort(403);
        }

        if ($user->isPlatformAdmin()) {
            return;
        }

        if ($visibility->canAccess($user, 'leads.listing')) {
            return;
        }

        if ($visibility->canAccess($user, 'leads.my_tasks') && $lead->isAssignedTo($user)) {
            return;
        }

        if ($visibility->canAccess($user, 'leads.design_tasks') && $lead->isDesignAssignedTo($user)) {
            return;
        }

        abort(403, 'You do not have access to this lead.');
    }

    private function authorizeLeadEdit(Lead $lead): void
    {
        $user = auth()->user();
        $visibility = app(VisibilityService::class);

        if (!$user) {
            abort(403);
        }

        if ($user->isPlatformAdmin() || $visibility->canAccess($user, 'leads.edit')) {
            return;
        }

        if ($visibility->canAccess($user, 'leads.edit.assigned') && $lead->isAssignedTo($user)) {
            return;
        }

        abort(403, 'You cannot edit this lead.');
    }

    private function authorizeVendorQuotes(Lead $lead): void
    {
        if (!can_manage_vendor_quotes($lead)) {
            abort(403, 'You cannot manage vendor quotes for this lead.');
        }
    }

    private function authorizeDesignTaskManage(Lead $lead): void
    {
        if (!$lead->design_assigned_to_user_id) {
            abort(404, 'No design task assigned for this lead.');
        }

        $user = auth()->user();
        $visibility = app(VisibilityService::class);

        if ($user->isPlatformAdmin()) {
            return;
        }

        if ($visibility->canAccess($user, 'leads.listing') || $visibility->canAccess($user, 'leads.dashboard')) {
            return;
        }

        if ($lead->isDesignAssignedTo($user)) {
            return;
        }

        if ($visibility->canAccess($user, 'leads.assign.design') && $lead->isAssignedTo($user)) {
            return;
        }

        abort(403, 'You cannot update this design task.');
    }
}
