<?php

namespace App\Http\Controllers;

use App\Models\ActivityLog;
use App\Models\Country;
use App\Models\Lead;
use App\Services\EmailService;
use App\Services\NotificationService;
use App\Services\SmsService;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Validator;

class LeadController extends Controller
{
    public function countries(): JsonResponse
    {
        $countries = Country::active()
            ->ordered()
            ->select('id', 'name', 'flag_image')
            ->get()
            ->map(fn ($c) => [
                'id' => $c->id,
                'name' => $c->name,
                'flag_url' => $c->flag_url,
            ]);

        return response()->json($countries);
    }

    public function sendOtp(Request $request): JsonResponse
    {
        $validator = Validator::make($request->all(), [
            'phone' => 'required|string|max:20',
        ]);

        if ($validator->fails()) {
            return response()->json([
                'success' => false,
                'errors' => $validator->errors(),
            ], 422);
        }

        $phone = $this->normalizeInternationalPhone($request->string('phone')->toString());
        if (! $phone) {
            return response()->json([
                'success' => false,
                'message' => 'Please enter a valid international phone number.',
            ], 422);
        }

        if ($this->hasBlockingLead($phone)) {
            return response()->json([
                'success' => false,
                'message' => 'This number already has an active application.',
                'field' => 'phone',
            ], 409);
        }

        $now = now();
        $existing = DB::table('lead_otp_verifications')
            ->where('phone', $phone)
            ->whereNull('verified_at')
            ->where('expires_at', '>', $now)
            ->orderByDesc('id')
            ->first();

        if ($existing && ! empty($existing->sent_at) && now()->diffInSeconds($existing->sent_at) < 30) {
            return response()->json([
                'success' => false,
                'message' => 'Please wait a few seconds before requesting another OTP.',
            ], 429);
        }

        $otp = SmsService::generateOtp();
        $smsService = app(SmsService::class);
        $sent = $smsService->sendOtp($phone, $otp);

        if (! $sent) {
            $providerMessage = $smsService->getLastError();
            return response()->json([
                'success' => false,
                'message' => $providerMessage
                    ? ('Could not send SMS OTP: ' . $providerMessage)
                    : 'Could not send SMS OTP now. Please try again.',
            ], 500);
        }

        DB::table('lead_otp_verifications')
            ->where('phone', $phone)
            ->whereNull('verified_at')
            ->update(['verified_at' => $now]);

        DB::table('lead_otp_verifications')->insert([
            'phone' => $phone,
            'code_hash' => $this->otpHash($otp),
            'attempts' => 0,
            'expires_at' => $now->copy()->addMinutes(5),
            'sent_at' => $now,
            'ip_address' => $request->ip(),
            'user_agent' => substr((string) $request->userAgent(), 0, 1000),
            'created_at' => $now,
            'updated_at' => $now,
        ]);

        return response()->json([
            'success' => true,
            'message' => 'OTP sent successfully.',
            'phone' => $phone,
        ]);
    }

    public function store(Request $request): JsonResponse
    {
        $validator = Validator::make($request->all(), [
            'phone' => 'required|string|max:20',
            'name' => 'required|string|max:150',
            'email' => 'required|email|max:200',
            'country_id' => 'nullable|integer|exists:countries,id',
            'otp' => 'required|string|size:4',
        ]);

        if ($validator->fails()) {
            return response()->json([
                'success' => false,
                'errors' => $validator->errors(),
            ], 422);
        }

        $phone = $this->normalizeInternationalPhone($request->string('phone')->toString());
        if (! $phone) {
            return response()->json([
                'success' => false,
                'message' => 'Please enter a valid international phone number.',
            ], 422);
        }

        if ($this->hasBlockingLead($phone)) {
            return response()->json([
                'success' => false,
                'message' => 'This number already has an active application.',
                'field' => 'phone',
            ], 409);
        }

        $otpRow = DB::table('lead_otp_verifications')
            ->where('phone', $phone)
            ->whereNull('verified_at')
            ->orderByDesc('id')
            ->first();

        if (! $otpRow || now()->greaterThan($otpRow->expires_at)) {
            return response()->json([
                'success' => false,
                'message' => 'OTP expired. Please request a new OTP.',
                'field' => 'otp',
            ], 422);
        }

        if ((int) $otpRow->attempts >= 5) {
            return response()->json([
                'success' => false,
                'message' => 'Too many invalid attempts. Request a new OTP.',
                'field' => 'otp',
            ], 422);
        }

        $isValidOtp = hash_equals($otpRow->code_hash, $this->otpHash($request->string('otp')->toString()));
        if (! $isValidOtp) {
            DB::table('lead_otp_verifications')
                ->where('id', $otpRow->id)
                ->update([
                    'attempts' => min(255, ((int) $otpRow->attempts) + 1),
                    'updated_at' => now(),
                ]);

            return response()->json([
                'success' => false,
                'message' => 'Invalid OTP. Please try again.',
                'field' => 'otp',
            ], 422);
        }

        DB::table('lead_otp_verifications')
            ->where('id', $otpRow->id)
            ->update([
                'verified_at' => now(),
                'updated_at' => now(),
            ]);

        $country = $request->country_id ? Country::find($request->country_id) : null;

        $lead = Lead::create([
            'phone' => $phone,
            'name' => $request->name,
            'email' => $request->email,
            'country_id' => $country?->id,
            'country_name' => $country?->name,
            'otp' => null,
            'otp_verified_at' => now(),
            'source' => $request->source,
            'utm_medium' => $request->utm_medium,
            'utm_campaign' => $request->utm_campaign,
            'page_url' => $request->page_url,
            'ip_address' => $request->ip(),
            'status' => 'new',
        ]);

        $adminLink = route('admin.leads.show', $lead);
        $leadLabel = $lead->name ?: $lead->phone;

        $adminEmailReport = app(NotificationService::class)->notifyAdmins(
            type: 'lead_submitted',
            title: 'New Lead Submitted',
            message: "New lead received: #{$lead->id} ({$leadLabel}).",
            actionUrl: $adminLink,
            sendEmail: true,
            emailSubject: 'New Lead Submitted',
            templateSubjectKey: 'lead_assigned_subject',
            templateBodyKey: 'lead_assigned_body',
            templateData: [
                'lead_id' => $lead->id,
                'lead_label' => $leadLabel,
                'action_url' => $adminLink,
                'recipient_name' => 'Admin',
            ]
        );

        $clientEmailResult = app(EmailService::class)->sendPlainEmailDetailed(
            to: (string) $lead->email,
            subject: 'Application Received',
            body: "Hi {$lead->name},\n\nThanks for submitting your application. Our team will contact you soon.\n\nReference: Lead #{$lead->id}"
        );

        $adminStatuses = array_map(fn ($r) => (string) ($r['email_status'] ?? 'failed'), $adminEmailReport);
        $allStatuses = array_filter(array_merge($adminStatuses, [(string) ($clientEmailResult['status'] ?? 'failed')]));
        $emailStatus = $this->aggregateEmailStatus($allStatuses);

        ActivityLog::create([
            'actor_user_id' => null,
            'event_type' => 'lead_submitted',
            'subject_type' => Lead::class,
            'subject_id' => $lead->id,
            'message' => "Lead #{$lead->id} submitted by {$lead->name} ({$lead->phone}).",
            'email_status' => $emailStatus,
            'meta' => [
                'admin_email_report' => $adminEmailReport,
                'client_email' => $lead->email,
                'client_email_result' => $clientEmailResult,
            ],
        ]);

        return response()->json([
            'success' => true,
            'message' => 'Application submitted successfully!',
            'lead_id' => $lead->id,
        ]);
    }

    private function normalizeInternationalPhone(string $phone): ?string
    {
        $raw = trim($phone);
        if ($raw === '') {
            return null;
        }

        $compact = preg_replace('/[\s\-\(\)]/', '', $raw) ?? '';
        if (str_starts_with($compact, '00')) {
            $compact = '+' . substr($compact, 2);
        }

        if (! preg_match('/^\+\d{8,15}$/', $compact)) {
            return null;
        }

        return $compact;
    }

    private function otpHash(string $otp): string
    {
        return hash_hmac('sha256', $otp, (string) config('app.key'));
    }

    private function hasBlockingLead(string $phone): bool
    {
        return Lead::query()
            ->where('phone', $phone)
            ->whereNotNull('otp_verified_at')
            ->whereIn('status', ['new', 'contacted', 'enrolled'])
            ->exists();
    }

    private function aggregateEmailStatus(array $statuses): string
    {
        if (empty($statuses)) {
            return 'skipped';
        }

        $unique = array_values(array_unique($statuses));
        if (count($unique) === 1) {
            return $unique[0];
        }

        if (in_array('sent', $unique, true) || in_array('simulated', $unique, true)) {
            return 'partial';
        }

        return 'failed';
    }
}
