<?php

namespace App\Http\Controllers\SuperAdmin;

use App\Http\Controllers\Controller;
use App\Models\Agent;
use App\Models\AgentCommission;
use App\Models\Tenant;
use App\Models\User;
use App\Services\ActivityLogger;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
use Illuminate\Validation\Rule;
use Illuminate\View\View;

class AgentController extends Controller
{
    public function index(Request $request): View
    {
        $search = trim((string) $request->query('q', ''));
        $status = $request->query('status', 'all');

        $agents = Agent::query()
            ->with('user')
            ->withCount('tenants')
            ->withSum(['commissions as pending_commissions_sum' => fn ($query) => $query->where('status', AgentCommission::STATUS_PENDING)], 'commission_amount')
            ->withSum(['commissions as paid_commissions_sum' => fn ($query) => $query->where('status', AgentCommission::STATUS_PAID)], 'commission_amount')
            ->when($status !== 'all', fn ($query) => $query->where('status', $status))
            ->when($search !== '', function ($query) use ($search): void {
                $like = '%'.str_replace(['\\', '%', '_'], ['\\\\', '\\%', '\\_'], $search).'%';

                $query->where(function ($query) use ($like): void {
                    $query->where('code', 'like', $like)
                        ->orWhere('slug', 'like', $like)
                        ->orWhere('business_name', 'like', $like)
                        ->orWhereHas('user', function ($query) use ($like): void {
                            $query->where('name', 'like', $like)
                                ->orWhere('email', 'like', $like)
                                ->orWhere('phone', 'like', $like);
                        });
                });
            })
            ->latest()
            ->paginate(12)
            ->withQueryString();

        return view('super.agents.index', [
            'agents' => $agents,
            'search' => $search,
            'status' => $status,
            'agentStatuses' => Agent::STATUSES,
            'merchantTypes' => Agent::MERCHANT_TYPES,
            'stats' => [
                'total' => Agent::query()->count(),
                'active' => Agent::query()->where('status', Agent::STATUS_ACTIVE)->count(),
                'pending' => Agent::query()->where('status', Agent::STATUS_PENDING)->count(),
                'tenants' => Tenant::query()->whereNotNull('agent_id')->count(),
                'pending_commission' => AgentCommission::query()->where('status', AgentCommission::STATUS_PENDING)->sum('commission_amount'),
                'paid_commission' => AgentCommission::query()->where('status', AgentCommission::STATUS_PAID)->sum('commission_amount'),
            ],
        ]);
    }

    public function store(Request $request): RedirectResponse
    {
        $validated = $request->validate([
            'name' => ['required', 'string', 'max:255'],
            'business_name' => ['nullable', 'string', 'max:255'],
            'email' => ['required', 'email', 'max:255', 'unique:users,email'],
            'phone' => ['nullable', 'string', 'max:50'],
            'country' => ['required', 'string', 'max:255'],
            'state' => ['required', 'string', 'max:255'],
            'city' => ['required', 'string', 'max:255'],
            'residential_address' => ['required', 'string', 'max:1000'],
            'merchant_type' => ['required', Rule::in(array_keys(Agent::MERCHANT_TYPES))],
            'onboarding_commission_rate' => ['required', 'numeric', 'min:0', 'max:'.Agent::MAX_COMMISSION_RATE],
            'referral_commission_rate' => ['required', 'numeric', 'min:0', 'max:'.Agent::MAX_COMMISSION_RATE],
            'status' => ['required', Rule::in(array_keys(Agent::STATUSES))],
            'bank_name' => ['nullable', 'string', 'max:255'],
            'account_name' => ['nullable', 'string', 'max:255'],
            'account_number' => ['nullable', 'string', 'max:30'],
            'password' => ['required', 'string', 'min:8', 'confirmed'],
            'notes' => ['nullable', 'string', 'max:2000'],
        ]);

        $agent = DB::transaction(function () use ($validated): Agent {
            $user = User::create([
                'role' => User::ROLE_AGENT,
                'name' => $validated['name'],
                'email' => $validated['email'],
                'phone' => $validated['phone'] ?? null,
                'password' => $validated['password'],
                'status' => 'active',
            ]);

            return Agent::create([
                'user_id' => $user->id,
                'code' => $this->uniqueCode(),
                'slug' => $this->uniqueSlug($validated['business_name'] ?: $validated['name']),
                'business_name' => $validated['business_name'] ?? null,
                'commission_rate' => Agent::SELF_ONBOARDING_COMMISSION_RATE,
                'merchant_type' => $validated['merchant_type'],
                'onboarding_commission_rate' => Agent::SELF_ONBOARDING_COMMISSION_RATE,
                'referral_commission_rate' => Agent::REFERRAL_COMMISSION_RATE,
                'status' => $validated['status'],
                'country' => filled($validated['country'] ?? null) ? $validated['country'] : 'Nigeria',
                'state' => $validated['state'] ?? null,
                'city' => $validated['city'] ?? null,
                'residential_address' => $validated['residential_address'] ?? null,
                'bank_name' => $validated['bank_name'] ?? null,
                'account_name' => $validated['account_name'] ?? null,
                'account_number' => $validated['account_number'] ?? null,
                'notes' => $validated['notes'] ?? null,
                'approved_at' => $validated['status'] === Agent::STATUS_ACTIVE ? now() : null,
                'suspended_at' => $validated['status'] === Agent::STATUS_SUSPENDED ? now() : null,
            ]);
        });

        ActivityLogger::log('merchant_created', $agent->displayName().' was created by super admin.', $agent);

        return redirect()->route('super.agents.show', $agent)->with('status', 'Merchant created.');
    }

    public function show(Agent $agent): View
    {
        $agent->load(['user', 'tenants.plan', 'commissions.tenant', 'commissions.plan', 'commissions.payment']);

        return view('super.agents.show', [
            'agent' => $agent,
            'agentStatuses' => Agent::STATUSES,
            'merchantTypes' => Agent::MERCHANT_TYPES,
            'availableTenants' => Tenant::query()
                ->with(['plan', 'agent.user'])
                ->orderBy('name')
                ->get(),
            'commissionStats' => [
                'pending' => AgentCommission::query()->where('agent_id', $agent->id)->where('status', AgentCommission::STATUS_PENDING)->sum('commission_amount'),
                'paid' => AgentCommission::query()->where('agent_id', $agent->id)->where('status', AgentCommission::STATUS_PAID)->sum('commission_amount'),
                'total' => AgentCommission::query()->where('agent_id', $agent->id)->sum('commission_amount'),
            ],
        ]);
    }

    public function update(Request $request, Agent $agent): RedirectResponse
    {
        $validated = $request->validate([
            'name' => ['required', 'string', 'max:255'],
            'business_name' => ['nullable', 'string', 'max:255'],
            'email' => ['required', 'email', 'max:255', Rule::unique('users', 'email')->ignore($agent->user_id)],
            'phone' => ['nullable', 'string', 'max:50'],
            'country' => ['required', 'string', 'max:255'],
            'state' => ['required', 'string', 'max:255'],
            'city' => ['required', 'string', 'max:255'],
            'residential_address' => ['required', 'string', 'max:1000'],
            'merchant_type' => ['required', Rule::in(array_keys(Agent::MERCHANT_TYPES))],
            'onboarding_commission_rate' => ['required', 'numeric', 'min:0', 'max:'.Agent::MAX_COMMISSION_RATE],
            'referral_commission_rate' => ['required', 'numeric', 'min:0', 'max:'.Agent::MAX_COMMISSION_RATE],
            'status' => ['required', Rule::in(array_keys(Agent::STATUSES))],
            'kyc_status' => ['required', Rule::in(array_keys(Agent::KYC_STATUSES))],
            'kyc_rejected_reason' => ['nullable', 'string', 'max:2000'],
            'bank_name' => ['nullable', 'string', 'max:255'],
            'account_name' => ['nullable', 'string', 'max:255'],
            'account_number' => ['nullable', 'string', 'max:30'],
            'password' => ['nullable', 'string', 'min:8', 'confirmed'],
            'notes' => ['nullable', 'string', 'max:2000'],
        ]);

        DB::transaction(function () use ($agent, $validated): void {
            $kycStatus = $validated['kyc_status'];

            $agent->user->update([
                'name' => $validated['name'],
                'email' => $validated['email'],
                'phone' => $validated['phone'] ?? null,
                'password' => filled($validated['password'] ?? null) ? $validated['password'] : $agent->user->password,
                'status' => $validated['status'] === Agent::STATUS_SUSPENDED ? 'inactive' : 'active',
            ]);

            $agent->update([
                'business_name' => $validated['business_name'] ?? null,
                'commission_rate' => Agent::SELF_ONBOARDING_COMMISSION_RATE,
                'merchant_type' => $validated['merchant_type'],
                'onboarding_commission_rate' => Agent::SELF_ONBOARDING_COMMISSION_RATE,
                'referral_commission_rate' => Agent::REFERRAL_COMMISSION_RATE,
                'status' => $validated['status'],
                'kyc_status' => $kycStatus,
                'kyc_verified_at' => $kycStatus === Agent::KYC_VERIFIED ? ($agent->kyc_verified_at ?: now()) : null,
                'kyc_rejected_reason' => $kycStatus === Agent::KYC_REJECTED ? ($validated['kyc_rejected_reason'] ?? null) : null,
                'country' => filled($validated['country'] ?? null) ? $validated['country'] : 'Nigeria',
                'state' => $validated['state'] ?? null,
                'city' => $validated['city'] ?? null,
                'residential_address' => $validated['residential_address'] ?? null,
                'bank_name' => $validated['bank_name'] ?? null,
                'account_name' => $validated['account_name'] ?? null,
                'account_number' => $validated['account_number'] ?? null,
                'notes' => $validated['notes'] ?? null,
                'approved_at' => $validated['status'] === Agent::STATUS_ACTIVE ? ($agent->approved_at ?: now()) : null,
                'suspended_at' => $validated['status'] === Agent::STATUS_SUSPENDED ? now() : null,
            ]);
        });

        ActivityLogger::log('merchant_updated', $agent->displayName().' was updated.', $agent);

        return back()->with('status', 'Merchant updated.');
    }

    public function assignTenants(Request $request, Agent $agent): RedirectResponse
    {
        $validated = $request->validate([
            'tenant_ids' => ['required', 'array'],
            'tenant_ids.*' => ['integer', 'exists:tenants,id'],
            'commission_type' => ['required', Rule::in([Agent::MERCHANT_ASSIGNED_ONBOARDING, Agent::MERCHANT_ONBOARDING, Agent::MERCHANT_REFERRAL])],
        ]);

        $commissionType = $validated['commission_type'] === Agent::MERCHANT_ONBOARDING
            ? Agent::MERCHANT_ASSIGNED_ONBOARDING
            : $validated['commission_type'];

        Tenant::query()
            ->whereIn('id', $validated['tenant_ids'])
            ->update([
                'agent_id' => $agent->id,
                'agent_assigned_at' => now(),
                'agent_commission_type' => $commissionType,
            ]);

        ActivityLogger::log('merchant_tenants_assigned', count($validated['tenant_ids']).' tenant(s) assigned to '.$agent->displayName().'.', $agent);

        return back()->with('status', 'Selected tenant(s) assigned to this merchant.');
    }

    public function unassignTenant(Agent $agent, Tenant $tenant): RedirectResponse
    {
        if ((int) $tenant->agent_id === (int) $agent->id) {
            $tenant->update([
                'agent_id' => null,
                'agent_assigned_at' => null,
                'agent_commission_type' => Agent::MERCHANT_ONBOARDING,
            ]);
        }

        ActivityLogger::log('merchant_tenant_unassigned', $tenant->name.' was removed from '.$agent->displayName().'.', $agent);

        return back()->with('status', 'Tenant removed from this merchant.');
    }

    public function markCommissionPaid(Agent $agent, AgentCommission $commission): RedirectResponse
    {
        abort_unless((int) $commission->agent_id === (int) $agent->id, 404);

        $commission->update([
            'status' => AgentCommission::STATUS_PAID,
            'paid_at' => now(),
        ]);

        ActivityLogger::log('merchant_commission_paid', 'Commission for '.$agent->displayName().' was marked as paid.', $commission);

        return back()->with('status', 'Commission marked as paid.');
    }

    private function uniqueSlug(string $name): string
    {
        $base = Str::slug($name) ?: 'merchant';
        $slug = $base;
        $counter = 2;

        while (Agent::query()->where('slug', $slug)->exists()) {
            $slug = $base.'-'.$counter++;
        }

        return $slug;
    }

    private function uniqueCode(): string
    {
        do {
            $code = 'MCH-'.Str::upper(Str::random(8));
        } while (Agent::query()->where('code', $code)->exists());

        return $code;
    }
}
