<?php

declare(strict_types=1);

namespace App\Controllers;

use App\Core\BaseController;
use App\Core\Database;
use App\Core\Request;
use App\Core\Response;
use App\Helpers\AjaxResponse;
use App\Helpers\Paginator;
use App\Helpers\UploadHelper;
use App\Models\CommentModel;
use App\Models\LeadModel;
use App\Models\NoteModel;
use App\Models\TagModel;
use App\Models\TaskModel;
use App\Services\ActivityLogger;
use App\Services\ExportService;
use App\Services\ImportService;
use App\Services\LeadService;

final class LeadController extends BaseController
{
    public function index(Request $request): Response
    {
        $model = new LeadModel();
        $filters = [
            'search' => $request->query('search'),
            'business_line_id' => $request->query('business_line_id'),
            'lead_stage_id' => $request->query('lead_stage_id'),
            'priority' => $request->query('priority'),
            'owner_id' => $request->query('owner_id'),
            'lead_source_id' => $request->query('lead_source_id'),
        ];

        if ($request->query('view') === 'pipeline') {
            return AjaxResponse::success($model->pipelineGrouped($filters));
        }

        $paginator = Paginator::fromRequest($request, defaultPerPage: 20);
        $rows = $model->paginatedList($filters, $paginator);

        if ($request->isAjax() || $request->isJson()) {
            return AjaxResponse::success(['rows' => $rows, 'meta' => $paginator->meta()]);
        }

        return $this->view('leads/index', [
            'title' => 'Leads',
            'activeNav' => 'leads',
            'breadcrumbs' => [['Home', 'dashboard'], ['CRM', 'leads'], ['Leads', 'leads']],
            'rows' => $rows,
            'meta' => $paginator->meta(),
            'lookups' => $this->lookups(),
        ]);
    }

    public function show(Request $request, array $params): Response
    {
        $id = (int) $params['id'];
        $model = new LeadModel();
        $lead = $model->findWithRelations($id);

        if ($lead === null) {
            return Response::html('404 Not Found', 404);
        }

        if ($request->isAjax() || $request->isJson()) {
            return AjaxResponse::success($lead);
        }

        return $this->view('leads/show', [
            'title' => trim($lead['first_name'] . ' ' . ($lead['last_name'] ?? '')),
            'activeNav' => 'leads',
            'breadcrumbs' => [['Home', 'dashboard'], ['Leads', 'leads'], ['Lead Details', 'leads/' . $id]],
            'lead' => $lead,
            'entityType' => 'lead',
            'entityId' => $id,
            'timeline' => ActivityLogger::timelineFor('lead', $id),
            'tasks' => (new TaskModel())->forLead($id),
            'notes' => (new NoteModel())->forEntity('lead', $id),
            'comments' => (new CommentModel())->forEntity('lead', $id),
            'attachments' => (new \App\Models\AttachmentModel())->forEntity('lead', $id),
            'tags' => (new TagModel())->forEntity('lead', $id),
            'emails' => (new \App\Models\EmailMessageModel())->forEntity('lead', $id),
            'entityEmail' => $lead['email'],
            'appointments' => (new \App\Models\AppointmentModel())->historyFor('lead', $id),
            'duplicates' => $model->findPotentialDuplicates($lead['email'], $lead['phone'], $id),
            'lookups' => $this->lookups(),
        ]);
    }

    public function store(Request $request): Response
    {
        $data = $this->validateOrFail($request, [
            'first_name' => 'required|string|max:100',
            'last_name' => 'nullable|string|max:100',
            'email' => 'nullable|email|max:191',
            'phone' => 'nullable|string|max:30',
            'business_line_id' => 'required|integer|exists:business_lines,id',
            'lead_source_id' => 'nullable|integer|exists:lead_sources,id',
            'lead_stage_id' => 'required|integer|exists:lead_stages,id',
            'priority' => 'nullable|in:low,medium,high,urgent',
            'estimated_value' => 'nullable|numeric',
            'notes' => 'nullable|string',
        ]);

        $model = new LeadModel();
        $duplicates = $model->findPotentialDuplicates($data['email'] ?? null, $data['phone'] ?? null);

        if ($duplicates !== [] && !$request->input('confirm_duplicate')) {
            return AjaxResponse::success(['duplicates' => $duplicates], 'Possible duplicate lead(s) found.', 409);
        }

        $data['currency'] = 'CAD';
        $id = $model->insert($data, $this->currentUserId());

        ActivityLogger::record('lead', $id, 'lead_created', 'Lead created', category: 'user_action');
        (new \App\Services\AutomationEngine())->fire('lead_created', 'lead', $id);

        return AjaxResponse::created($model->findWithRelations($id), 'Lead created.');
    }

    public function update(Request $request, array $params): Response
    {
        $data = $this->validateOrFail($request, [
            'first_name' => 'required|string|max:100',
            'last_name' => 'nullable|string|max:100',
            'email' => 'nullable|email|max:191',
            'phone' => 'nullable|string|max:30',
            'business_line_id' => 'required|integer|exists:business_lines,id',
            'lead_source_id' => 'nullable|integer|exists:lead_sources,id',
            'company_id' => 'nullable|integer|exists:companies,id',
            'priority' => 'nullable|in:low,medium,high,urgent',
            'owner_id' => 'nullable|integer|exists:users,id',
            'estimated_value' => 'nullable|numeric',
            'notes' => 'nullable|string',
        ]);

        $id = (int) $params['id'];
        $model = new LeadModel();
        $model->update($id, $data, $this->currentUserId());

        ActivityLogger::record('lead', $id, 'lead_updated', 'Lead details updated');

        return AjaxResponse::success($model->findWithRelations($id), 'Lead updated.');
    }

    public function moveStage(Request $request, array $params): Response
    {
        $id = (int) $params['id'];
        $stageId = (int) $request->input('lead_stage_id');

        $model = new LeadModel();
        $before = $model->find($id);
        $model->moveStage($id, $stageId, $this->currentUserId());
        $after = $model->findWithRelations($id);

        ActivityLogger::record(
            'lead',
            $id,
            'stage_changed',
            "Moved to stage \"{$after['stage_name']}\"",
            ['from_stage_id' => $before['lead_stage_id'] ?? null, 'to_stage_id' => $stageId]
        );

        return AjaxResponse::success($after, 'Stage updated.');
    }

    public function destroy(Request $request, array $params): Response
    {
        $id = (int) $params['id'];
        (new LeadModel())->delete($id, $this->currentUserId());
        ActivityLogger::record('lead', $id, 'lead_deleted', 'Lead moved to trash');

        return AjaxResponse::success(null, 'Lead moved to trash.');
    }

    public function restore(Request $request, array $params): Response
    {
        $id = (int) $params['id'];
        (new LeadModel())->restore($id);
        ActivityLogger::record('lead', $id, 'lead_restored', 'Lead restored from trash', category: 'admin');

        return AjaxResponse::success(null, 'Lead restored.');
    }

    /**
     * Bulk actions on the list view — every competitor CRM (HubSpot,
     * Salesforce, Zoho, Monday, GoHighLevel) supports select-many +
     * act-once on a list; this is A&B Hub's. One endpoint, an
     * `action` field picks the operation, kept to the handful of
     * genuinely common bulk operations rather than a generic
     * "bulk-update any field" surface.
     */
    public function bulkAction(Request $request): Response
    {
        $data = $this->validateOrFail($request, [
            'ids' => 'required|array',
            'action' => 'required|in:assign_owner,change_stage,delete,add_tag',
        ]);

        $ids = array_map('intval', $data['ids']);
        if ($ids === []) {
            return AjaxResponse::error('No leads selected.', 422);
        }

        $model = new LeadModel();
        $affected = 0;

        switch ($data['action']) {
            case 'assign_owner':
                $ownerId = $request->input('owner_id') !== '' ? (int) $request->input('owner_id') : null;
                foreach ($ids as $id) {
                    $model->update($id, ['owner_id' => $ownerId], $this->currentUserId());
                    $affected++;
                }
                ActivityLogger::logAction('Bulk-assigned ' . count($ids) . ' lead(s)', 'user_action');
                break;

            case 'change_stage':
                $stageId = (int) $request->input('lead_stage_id');
                if ($stageId <= 0) {
                    return AjaxResponse::error('lead_stage_id is required for change_stage.', 422);
                }
                foreach ($ids as $id) {
                    $model->update($id, ['lead_stage_id' => $stageId], $this->currentUserId());
                    $affected++;
                }
                ActivityLogger::logAction('Bulk stage-changed ' . count($ids) . ' lead(s)', 'user_action');
                break;

            case 'delete':
                foreach ($ids as $id) {
                    $model->delete($id, $this->currentUserId());
                    $affected++;
                }
                ActivityLogger::logAction('Bulk-deleted ' . count($ids) . ' lead(s)', 'user_action');
                break;

            case 'add_tag':
                $tagId = (int) $request->input('tag_id');
                if ($tagId <= 0) {
                    return AjaxResponse::error('tag_id is required for add_tag.', 422);
                }
                $tagModel = new TagModel();
                foreach ($ids as $id) {
                    $tagModel->attach($tagId, 'lead', $id);
                    $affected++;
                }
                ActivityLogger::logAction('Bulk-tagged ' . count($ids) . ' lead(s)', 'user_action');
                break;
        }

        return AjaxResponse::success(['affected' => $affected], "{$affected} lead(s) updated.");
    }

    public function trashed(Request $request): Response
    {
        $stmt = Database::connection()->prepare(
            "SELECT l.*, CONCAT(u.first_name, ' ', u.last_name) AS owner_name FROM leads l
             LEFT JOIN users u ON u.id = l.owner_id
             WHERE l.deleted_at IS NOT NULL AND l.merged_into_lead_id IS NULL
             ORDER BY l.deleted_at DESC LIMIT 100"
        );
        $stmt->execute();

        return AjaxResponse::success($stmt->fetchAll());
    }

    public function convert(Request $request, array $params): Response
    {
        $id = (int) $params['id'];
        $customerId = (new LeadService())->convertToCustomer($id, $request->all(), $this->currentUserId());

        return AjaxResponse::success(['customer_id' => $customerId], 'Lead converted to customer.');
    }

    public function merge(Request $request, array $params): Response
    {
        $survivorId = (int) $params['id'];
        $duplicateId = (int) $request->input('duplicate_lead_id');

        (new LeadService())->merge($survivorId, $duplicateId, $this->currentUserId());

        return AjaxResponse::success(null, 'Leads merged successfully.');
    }

    public function importForm(Request $request): Response
    {
        return $this->view('leads/import', [
            'title' => 'Import Leads',
            'activeNav' => 'leads',
            'breadcrumbs' => [['Home', 'dashboard'], ['Leads', 'leads'], ['Import', 'leads/import']],
            'lookups' => $this->lookups(),
        ]);
    }

    public function import(Request $request): Response
    {
        $file = $request->file('file');
        if ($file === null) {
            return AjaxResponse::error('No file was uploaded.', 422);
        }

        $uploader = UploadHelper::make();
        $stored = $uploader->store($file, 'imports');
        if ($stored === null) {
            return AjaxResponse::error('Upload failed.', 422, $uploader->errors());
        }

        $businessLineId = (int) $request->input('business_line_id');
        $absolutePath = dirname(__DIR__, 2) . '/uploads/' . $stored['disk_path'];

        $result = (new ImportService())->importLeads($absolutePath, $businessLineId, $this->currentUserId());

        return AjaxResponse::success($result, "Import complete: {$result['created']} created, {$result['skipped']} skipped, {$result['failed']} failed.");
    }

    public function export(Request $request): Response
    {
        $filters = [
            'search' => $request->query('search'),
            'business_line_id' => $request->query('business_line_id'),
            'lead_stage_id' => $request->query('lead_stage_id'),
        ];

        $csv = (new ExportService())->exportLeads($filters);

        return Response::html($csv)
            ->withHeader('Content-Type', 'text/csv; charset=UTF-8')
            ->withHeader('Content-Disposition', 'attachment; filename="leads-' . date('Y-m-d') . '.csv"');
    }

    private function lookups(): array
    {
        $pdo = Database::connection();

        return [
            'business_lines' => $pdo->query('SELECT id, name FROM business_lines ORDER BY name')->fetchAll(),
            'stages' => $pdo->query('SELECT id, name, slug, color_hex FROM lead_stages WHERE is_active = 1 ORDER BY sort_order')->fetchAll(),
            'sources' => $pdo->query('SELECT id, name FROM lead_sources WHERE is_active = 1 ORDER BY sort_order')->fetchAll(),
            'owners' => $pdo->query("SELECT id, CONCAT(first_name, ' ', last_name) AS name FROM users WHERE deleted_at IS NULL AND status = 'active' ORDER BY first_name")->fetchAll(),
            'companies' => (new \App\Models\CompanyModel())->forSelect(),
        ];
    }
}
