<?php

namespace App\Http\Controllers\Admin;

use App\Http\Controllers\Controller;
use App\Models\Lead;
use App\Models\LeadRule;
use App\Models\SiteSetting;
use App\Support\LeadBalanceLedger;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
use Symfony\Component\HttpFoundation\StreamedResponse;

class LeadsController extends Controller
{
    public function index(Request $request)
    {
        $query = $this->filteredQuery($request);

        $statsQuery = clone $query;
        $stats = [
            'total' => (clone $statsQuery)->count(),
            'approved' => (clone $statsQuery)->where('status', 'approved')->count(),
            'held' => (clone $statsQuery)->where('status', 'held')->count(),
            'rejected' => (clone $statsQuery)->where('status', 'rejected')->count(),
            'chargeback' => (clone $statsQuery)->where('status', 'chargeback')->count(),
            'points' => (clone $statsQuery)->sum('credited_points'),
            'payout' => (clone $statsQuery)->sum('payout'),
        ];

        $leads = $query
            ->with('adjustments.admin')
            ->latest()
            ->paginate((int) $request->input('per_page', 15))
            ->withQueryString();

        $providers = Lead::query()
            ->select('company')
            ->whereNotNull('company')
            ->distinct()
            ->orderBy('company')
            ->pluck('company');

        $rules = LeadRule::orderBy('sort_order')->orderBy('id')->get();
        $defaultStatus = site_setting('leads_default_status', 'approved');

        return view('admin.leads', compact('leads', 'stats', 'providers', 'rules', 'defaultStatus'));
    }

    public function storeRule(Request $request)
    {
        $data = $request->validate([
            'match_field' => ['required', 'in:'.implode(',', LeadRule::FIELDS)],
            'match_operator' => ['required', 'in:'.implode(',', LeadRule::OPERATORS)],
            'match_value' => ['required', 'string', 'max:255'],
            'action' => ['required', 'in:'.implode(',', LeadRule::ACTIONS)],
        ]);

        LeadRule::create($data + ['is_active' => true, 'sort_order' => (int) LeadRule::max('sort_order') + 1]);

        return redirect()->route('admin.leads')->with('success', 'Auto-approve rule added.');
    }

    public function destroyRule(LeadRule $rule)
    {
        $rule->delete();

        return redirect()->route('admin.leads')->with('success', 'Rule removed.');
    }

    public function toggleRule(LeadRule $rule)
    {
        $rule->update(['is_active' => !$rule->is_active]);

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

    public function saveDefaultStatus(Request $request)
    {
        $data = $request->validate([
            'leads_default_status' => ['required', 'in:approved,hold'],
            'leads_auto_approve_hours' => ['nullable', 'integer', 'min:0', 'max:720'],
        ]);

        SiteSetting::put('leads_default_status', $data['leads_default_status']);
        SiteSetting::put('leads_auto_approve_hours', (int) ($data['leads_auto_approve_hours'] ?? 0));
        Cache::flush();

        return redirect()->route('admin.leads')->with('success', 'Default lead status saved.');
    }

    public function export(Request $request): StreamedResponse
    {
        $filename = 'basketloot-leads-' . now()->format('Y-m-d-His') . '.csv';
        $leads = $this->filteredQuery($request)->latest()->get();

        return response()->streamDownload(function () use ($leads) {
            $handle = fopen('php://output', 'w');
            fputcsv($handle, ['ID', 'User', 'Email', 'Company', 'Type', 'Status', 'Offer ID', 'Offer Name', 'Original Points', 'Credited Points', 'Payout', 'IP', 'Country', 'TRX', 'Created']);

            foreach ($leads as $lead) {
                fputcsv($handle, [
                    $lead->id,
                    optional($lead->user)->username,
                    optional($lead->user)->email,
                    $lead->company,
                    $lead->type,
                    $lead->status,
                    $lead->offer_id,
                    html_entity_decode((string) $lead->offer_name),
                    $lead->points,
                    $lead->credited_points,
                    $lead->payout,
                    $lead->ip,
                    $lead->country,
                    $lead->trx,
                    optional($lead->created_at)->toDateTimeString(),
                ]);
            }

            fclose($handle);
        }, $filename, ['Content-Type' => 'text/csv']);
    }

    public function update(Request $request, Lead $lead)
    {
        $data = $request->validate([
            'status' => ['required', 'in:approved,held,rejected,chargeback'],
            'type' => ['required', 'in:offer,survey,reward,bonus'],
            'admin_note' => ['nullable', 'string', 'max:500'],
            'reverse_points' => ['nullable', 'boolean'],
            'balance_action' => ['nullable', 'in:none,release,chargeback'],
            'adjustment_amount' => ['nullable', 'numeric', 'gt:0'],
            'adjustment_unit' => ['nullable', 'in:coins,usd'],
            'adjustment_token' => ['required', 'string', 'max:190'],
        ]);

        if (($data['balance_action'] ?? 'none') === 'release' && empty($data['adjustment_amount'])) {
            return back()->withErrors(['adjustment_amount' => 'Enter the coin or USD amount to release.'])->withInput();
        }

        DB::transaction(function () use ($lead, $data) {
            $this->applyLeadReview($lead, $data);
        });

        return redirect()->route('admin.leads', request()->query())->with('success', 'Lead updated successfully.');
    }

    public function bulk(Request $request)
    {
        $data = $request->validate([
            'ids' => ['required', 'array', 'min:1'],
            'ids.*' => ['integer', 'exists:leads,id'],
            'action' => ['required', 'in:approve,hold,reject,chargeback'],
        ]);

        $status = ['approve' => 'approved', 'hold' => 'held', 'reject' => 'rejected', 'chargeback' => 'chargeback'][$data['action']];

        DB::transaction(function () use ($data, $status) {
            Lead::whereIn('id', $data['ids'])->get()->each(function (Lead $lead) use ($status) {
                $this->applyLeadReview($lead, [
                    'status' => $status,
                    'type' => $lead->type ?: 'offer',
                    'admin_note' => match ($status) {
                        'held' => 'Held for manual review.',
                        'rejected' => 'Rejected from bulk review.',
                        'chargeback' => 'Fully charged back from bulk review.',
                        default => 'Approved from bulk review.',
                    },
                    'reverse_points' => $status === 'rejected',
                    'balance_action' => $status === 'chargeback' ? 'chargeback' : 'none',
                    'adjustment_token' => (string) Str::uuid(),
                ]);
            });
        });

        return redirect()->route('admin.leads', request()->query())->with('success', 'Bulk action applied.');
    }

    private function filteredQuery(Request $request)
    {
        return Lead::query()
            ->with('user')
            ->when($request->filled('search'), function ($query) use ($request) {
                $search = $request->input('search');
                $query->where(function ($nested) use ($search) {
                    $nested->where('company', 'like', "%{$search}%")
                        ->orWhere('offer_id', 'like', "%{$search}%")
                        ->orWhere('offer_name', 'like', "%{$search}%")
                        ->orWhere('trx', 'like', "%{$search}%")
                        ->orWhere('ip', 'like', "%{$search}%")
                        ->orWhereHas('user', function ($userQuery) use ($search) {
                            $userQuery->where('username', 'like', "%{$search}%")
                                ->orWhere('email', 'like', "%{$search}%");
                        });
                });
            })
            ->when($request->filled('status'), fn ($query) => $query->where('status', $request->input('status')))
            ->when($request->filled('type'), fn ($query) => $query->where('type', $request->input('type')))
            ->when($request->filled('provider'), fn ($query) => $query->where('company', $request->input('provider')))
            ->when($request->filled('date_from'), fn ($query) => $query->whereDate('created_at', '>=', $request->input('date_from')))
            ->when($request->filled('date_to'), fn ($query) => $query->whereDate('created_at', '<=', $request->input('date_to')));
    }

    private function applyLeadReview(Lead $lead, array $data): void
    {
        $lead = Lead::whereKey($lead->id)->lockForUpdate()->firstOrFail();
        $action = $data['balance_action'] ?? 'none';
        $unit = $data['adjustment_unit'] ?? 'coins';
        $requested = isset($data['adjustment_amount']) ? (float) $data['adjustment_amount'] : null;
        $amount = $requested === null ? null : round($requested * ($unit === 'usd' ? coins_per_usd() : 1), 4);
        $ledger = app(LeadBalanceLedger::class);

        if ($action === 'release') {
            $adjustment = $ledger->adjust(
                $lead,
                (float) $amount,
                'release',
                $data['adjustment_token'],
                auth()->user(),
                $unit,
                $requested,
                $data['admin_note'] ?? null
            );
            $original = (float) ($lead->original_points ?? $lead->points);
            $data['status'] = $adjustment->credited_after >= $original ? 'approved' : 'held';
        } elseif ($action === 'chargeback') {
            $chargeback = $amount ?? (float) $lead->credited_points;
            if ($chargeback > 0) {
                $adjustment = $ledger->adjust(
                    $lead,
                    -$chargeback,
                    'chargeback',
                    $data['adjustment_token'],
                    auth()->user(),
                    $unit,
                    $requested,
                    $data['admin_note'] ?? null
                );
                $original = (float) ($lead->original_points ?? $lead->points);
                $data['status'] = $adjustment->credited_after <= 0
                    ? 'chargeback'
                    : ($adjustment->credited_after < $original ? 'held' : 'approved');
            }
        } elseif ($data['status'] === 'approved' && (float) $lead->credited_points <= 0) {
            $this->awardLeadPoints($lead, null, $data['adjustment_token'], auth()->user(), $data['admin_note'] ?? null);
        } elseif (in_array($data['status'], ['rejected', 'chargeback'], true) && !empty($data['reverse_points']) && (float) $lead->credited_points > 0) {
            $this->reverseLeadPoints($lead, null, $data['adjustment_token'], auth()->user(), $data['admin_note'] ?? null);
        }

        $lead->fill([
            'status' => $data['status'],
            'type' => $data['type'],
            'admin_note' => array_key_exists('admin_note', $data) ? $data['admin_note'] : $lead->admin_note,
            'reviewed_at' => now(),
            'reviewed_by' => auth()->id(),
        ]);

        $lead->save();
    }
}
