<?php

namespace App\Controllers\Api\V1;

use App\Controllers\BaseController;
use App\Models\AgencyModel;
use App\Services\Support\ActivityLogger;
use App\Services\Support\AuditService;
use Config\Database;

/**
 * Super Admin only — enforced via roleguard:agencies.* on the routes.
 * This was referenced by the admin panel routes/permission matrix from
 * early in the build but never actually implemented until now.
 */
class AdminAgencyController extends BaseController
{
    private AgencyModel $agencies;
    private AuditService $audit;
    private ActivityLogger $activity;

    public function initController(\CodeIgniter\HTTP\RequestInterface $request, \CodeIgniter\HTTP\ResponseInterface $response, \Psr\Log\LoggerInterface $logger)
    {
        parent::initController($request, $response, $logger);
        $this->agencies = new AgencyModel();
        $this->audit    = new AuditService();
        $this->activity = new ActivityLogger();
    }

    public function index()
    {
        $status = $this->request->getGet('status');
        $verification = $this->request->getGet('verification_status');
        $q = $this->request->getGet('q');
        $page = max(1, (int) ($this->request->getGet('page') ?? 1));
        $perPage = min(50, max(5, (int) ($this->request->getGet('per_page') ?? 15)));

        $builder = $this->agencies->builder();
        if ($status) {
            $builder->where('status', $status);
        }
        if ($verification) {
            $builder->where('verification_status', $verification);
        }
        if ($q) {
            $builder->groupStart()->like('company_name', $q)->orLike('email', $q)->groupEnd();
        }

        $total = $builder->countAllResults(false);
        $agencies = $builder->orderBy('created_at', 'DESC')->limit($perPage, ($page - 1) * $perPage)->get()->getResultArray();

        // attach owner name/email + recruiter count for the panel table
        $db = Database::connect();
        foreach ($agencies as &$agency) {
            $owner = $db->table('users')->select('name, email')->where('id', $agency['owner_user_id'])->get()->getRowArray();
            $agency['owner_name'] = $owner['name'] ?? null;
            $agency['owner_email'] = $owner['email'] ?? null;
            $agency['recruiter_count'] = $db->table('users')->where('agency_id', $agency['id'])->where('status', 'active')->countAllResults();

            $creditRow = $db->table('credit_balances')->select('balance')->where('agency_id', $agency['id'])->get()->getRowArray();
            $agency['credit_balance'] = $creditRow['balance'] ?? 0;
        }

        return $this->jsonSuccess($agencies, ['page' => $page, 'per_page' => $perPage, 'total' => $total]);
    }

    public function show(int $id)
    {
        $agency = $this->agencies->find($id);
        if (! $agency) {
            return $this->jsonError([['code' => 'NOT_FOUND', 'message' => 'Agency not found.']], 404);
        }

        $docs = Database::connect()->table('agency_verification_documents')->where('agency_id', $id)->get()->getResultArray();

        return $this->jsonSuccess(['agency' => $agency->toArray(), 'documents' => $docs]);
    }

    public function verify(int $id)
    {
        $agency = $this->agencies->find($id);
        if (! $agency) {
            return $this->jsonError([['code' => 'NOT_FOUND', 'message' => 'Agency not found.']], 404);
        }

        $input = $this->request->getJSON(true);
        $decision = $input['decision'] ?? null; // 'verified' or 'rejected'

        if (! in_array($decision, ['verified', 'rejected'], true)) {
            return $this->jsonValidationError(['decision' => 'decision must be "verified" or "rejected".']);
        }

        $before = ['verification_status' => $agency->verification_status];
        $this->agencies->update($id, ['verification_status' => $decision]);

        $this->audit->record($this->currentUser()->id, 'agencies', 'verify', $id, $before, ['verification_status' => $decision]);
        $this->activity->log($this->currentUser()->id, null, 'super_admin', 'agency_verification', ['agency_id' => $id, 'decision' => $decision]);

        return $this->jsonSuccess(['agency' => $this->agencies->find($id)->toArray()]);
    }

    public function suspend(int $id)
    {
        $agency = $this->agencies->find($id);
        if (! $agency) {
            return $this->jsonError([['code' => 'NOT_FOUND', 'message' => 'Agency not found.']], 404);
        }

        $before = ['status' => $agency->status];
        $this->agencies->update($id, ['status' => 'suspended']);

        $this->audit->record($this->currentUser()->id, 'agencies', 'suspend', $id, $before, ['status' => 'suspended']);
        $this->activity->log($this->currentUser()->id, null, 'super_admin', 'agency_suspend', ['agency_id' => $id]);

        return $this->jsonSuccess(['agency' => $this->agencies->find($id)->toArray()]);
    }

    /**
     * All pending plan-change requests awaiting Super Admin review.
     */
    public function pendingPlanRequests()
    {
        $db = \Config\Database::connect();
        $requests = $db->table('plan_change_requests pcr')
            ->select('pcr.*, a.company_name, u.name as requested_by_name, cp.name as current_plan_name, rp.name as requested_plan_name, rp.price_monthly as requested_plan_price')
            ->join('agencies a', 'a.id = pcr.agency_id')
            ->join('users u', 'u.id = pcr.requested_by_user_id')
            ->join('subscription_plans rp', 'rp.id = pcr.requested_plan_id')
            ->join('subscription_plans cp', 'cp.id = pcr.current_plan_id', 'left')
            ->where('pcr.status', 'pending')
            ->orderBy('pcr.created_at', 'ASC')
            ->get()->getResultArray();

        return $this->jsonSuccess(['requests' => $requests]);
    }

    public function approvePlanChange(int $requestId)
    {
        $requestModel = new \App\Models\PlanChangeRequestModel();
        $request = $requestModel->find($requestId);
        if (! $request || $request['status'] !== 'pending') {
            return $this->jsonError([['code' => 'NOT_FOUND', 'message' => 'No pending request found.']], 404);
        }

        $plan = (new \App\Models\SubscriptionPlanModel())->find($request['requested_plan_id']);
        if (! $plan) {
            return $this->jsonError([['code' => 'INVALID_PLAN', 'message' => 'The requested plan no longer exists.']], 422);
        }

        try {
            (new \App\Services\Billing\SubscriptionService())->changePlan($request['agency_id'], $plan['slug'], $request['billing_cycle'], $this->currentUser()->id);
        } catch (\InvalidArgumentException $e) {
            return $this->jsonError([['code' => 'PLAN_CHANGE_FAILED', 'message' => $e->getMessage()]], 422);
        }

        $requestModel->update($requestId, [
            'status'              => 'approved',
            'reviewed_by_user_id' => $this->currentUser()->id,
            'reviewed_at'         => date('Y-m-d H:i:s'),
        ]);

        $agency = (new \App\Models\AgencyModel())->find($request['agency_id']);
        (new \App\Services\Notification\NotificationService())->send(
            $request['requested_by_user_id'],
            'plan_change_approved',
            ['plan_name' => $plan['name']]
        );

        $this->audit->record($this->currentUser()->id, 'plan_change_requests', 'approve', $requestId, ['status' => 'pending'], ['status' => 'approved']);

        return $this->jsonSuccess(['message' => "Plan changed to {$plan['name']} for {$agency->company_name}."]);
    }

    public function rejectPlanChange(int $requestId)
    {
        $requestModel = new \App\Models\PlanChangeRequestModel();
        $request = $requestModel->find($requestId);
        if (! $request || $request['status'] !== 'pending') {
            return $this->jsonError([['code' => 'NOT_FOUND', 'message' => 'No pending request found.']], 404);
        }

        $input = $this->request->getJSON(true);

        $requestModel->update($requestId, [
            'status'              => 'rejected',
            'reviewed_by_user_id' => $this->currentUser()->id,
            'reviewed_at'         => date('Y-m-d H:i:s'),
            'note'                => $input['note'] ?? null,
        ]);

        $plan = (new \App\Models\SubscriptionPlanModel())->find($request['requested_plan_id']);
        (new \App\Services\Notification\NotificationService())->send(
            $request['requested_by_user_id'],
            'plan_change_rejected',
            ['plan_name' => $plan['name'] ?? 'the requested plan', 'note' => $input['note'] ?? '']
        );

        $this->audit->record($this->currentUser()->id, 'plan_change_requests', 'reject', $requestId, ['status' => 'pending'], ['status' => 'rejected']);

        return $this->jsonSuccess(['message' => 'Request rejected.']);
    }

    /**
     * Resets the agency OWNER's password to a freshly generated random
     * one and returns it once in the response — there's no reliable
     * outbound email/SMS gateway configured in this build to deliver a
     * reset link, so the Super Admin sees the new password directly and
     * shares it with the client through whatever channel they use.
     */
    public function resetPassword(int $id)
    {
        $agency = $this->agencies->find($id);
        if (! $agency) {
            return $this->jsonError([['code' => 'NOT_FOUND', 'message' => 'Agency not found.']], 404);
        }

        $ownerUserId = $agency->owner_user_id;
        if (! $ownerUserId) {
            return $this->jsonError([['code' => 'NO_OWNER', 'message' => 'This agency has no owner account on file.']], 422);
        }

        // Random, readable temp password — not a token the admin has to
        // decode, just something they can read aloud or paste directly.
        $tempPassword = bin2hex(random_bytes(4)) . '-' . bin2hex(random_bytes(2));

        $userModel = new \App\Models\UserModel();
        $userModel->update($ownerUserId, ['password_hash' => password_hash($tempPassword, PASSWORD_ARGON2ID)]);

        $this->audit->record($this->currentUser()->id, 'users', 'password_reset', $ownerUserId, [], ['reset_by' => 'super_admin']);
        $this->activity->log($this->currentUser()->id, $id, 'super_admin', 'agency_password_reset', ['agency_id' => $id]);

        return $this->jsonSuccess(['temp_password' => $tempPassword, 'owner_email' => $agency->email]);
    }

    public function activate(int $id)
    {
        $agency = $this->agencies->find($id);
        if (! $agency) {
            return $this->jsonError([['code' => 'NOT_FOUND', 'message' => 'Agency not found.']], 404);
        }

        $before = ['status' => $agency->status];
        $this->agencies->update($id, ['status' => 'active']);

        $this->audit->record($this->currentUser()->id, 'agencies', 'activate', $id, $before, ['status' => 'active']);

        return $this->jsonSuccess(['agency' => $this->agencies->find($id)->toArray()]);
    }

    /**
     * Shows the agency's current subscription plan + usage — surfaced on
     * the Agencies page so Super Admin can see why a "plan limit reached"
     * error is happening before deciding whether to reset usage or
     * change their plan.
     */
    public function subscriptionInfo(int $id)
    {
        $agency = $this->agencies->find($id);
        if (! $agency) {
            return $this->jsonError([['code' => 'NOT_FOUND', 'message' => 'Agency not found.']], 404);
        }

        $context = (new \App\Services\Billing\SubscriptionService())->currentSubscriptionWithPlan($id);
        if (! $context) {
            return $this->jsonSuccess(['subscription' => null]);
        }

        $allPlans = (new \App\Models\SubscriptionPlanModel())->where('is_active', 1)->orderBy('sort_order', 'ASC')->findAll();

        return $this->jsonSuccess([
            'subscription' => $context['subscription'],
            'plan'         => $context['plan'],
            'usage'        => $context['usage'],
            'all_plans'    => $allPlans,
        ]);
    }

    /**
     * Super Admin plan override — assigns a plan directly, bypassing
     * payment (this is an administrative grant, not a purchase). Reuses
     * SubscriptionService::changePlan() so the resulting subscription
     * record is indistinguishable from a normal upgrade.
     */
    public function changeSubscriptionPlan(int $id)
    {
        $agency = $this->agencies->find($id);
        if (! $agency) {
            return $this->jsonError([['code' => 'NOT_FOUND', 'message' => 'Agency not found.']], 404);
        }

        $input = $this->request->getJSON(true);
        $planSlug = $input['plan_slug'] ?? null;
        $billingCycle = $input['billing_cycle'] ?? 'monthly';

        if (! $planSlug) {
            return $this->jsonValidationError(['plan_slug' => 'A plan is required.']);
        }

        try {
            $subscription = (new \App\Services\Billing\SubscriptionService())->changePlan($id, $planSlug, $billingCycle, $this->currentUser()->id);
        } catch (\InvalidArgumentException $e) {
            return $this->jsonError([['code' => 'INVALID_PLAN', 'message' => $e->getMessage()]], 422);
        }

        return $this->jsonSuccess(['subscription' => $subscription]);
    }

    /**
     * Super Admin usage reset — see SubscriptionService::resetUsage()
     * for when this is the right tool vs. changing the plan outright.
     */
    public function resetSubscriptionUsage(int $id)
    {
        $agency = $this->agencies->find($id);
        if (! $agency) {
            return $this->jsonError([['code' => 'NOT_FOUND', 'message' => 'Agency not found.']], 404);
        }

        try {
            (new \App\Services\Billing\SubscriptionService())->resetUsage($id, $this->currentUser()->id);
        } catch (\RuntimeException $e) {
            return $this->jsonError([['code' => 'NO_SUBSCRIPTION', 'message' => $e->getMessage()]], 422);
        }

        return $this->jsonSuccess(['message' => 'Usage reset for the current period.']);
    }

    /**
     * Manual credit grant/deduction — a mandatory note is required so
     * there's always a human-readable reason attached to the ledger entry
     * (audit_logs already captures who/when/what, this note captures why).
     */
    public function adjustCredits(int $id)
    {
        $agency = $this->agencies->find($id);
        if (! $agency) {
            return $this->jsonError([['code' => 'NOT_FOUND', 'message' => 'Agency not found.']], 404);
        }

        $input = $this->request->getJSON(true);
        $amount = (int) ($input['amount'] ?? 0);
        $note = trim((string) ($input['note'] ?? ''));

        if ($amount === 0) {
            return $this->jsonValidationError(['amount' => 'Amount must be a non-zero integer (positive to add, negative to deduct).']);
        }
        if ($note === '') {
            return $this->jsonValidationError(['note' => 'A note explaining this adjustment is required.']);
        }

        $credits = new \App\Services\Billing\CreditService();

        try {
            if ($amount > 0) {
                $credits->credit($id, $amount, 'bonus', 'admin_manual_adjustment', $this->currentUser()->id);
            } else {
                $credits->debit($id, abs($amount), 'admin_manual_adjustment', $this->currentUser()->id, $this->currentUser()->id);
            }
        } catch (\App\Exceptions\InsufficientCreditsException $e) {
            return $this->jsonError([['code' => 'INSUFFICIENT_CREDITS', 'message' => $e->getMessage()]], 422);
        }

        $this->audit->record($this->currentUser()->id, 'credits', 'manual_adjustment', $id, null, ['amount' => $amount, 'note' => $note]);
        $this->activity->log($this->currentUser()->id, $id, 'super_admin', 'credit_manual_adjustment', ['amount' => $amount, 'note' => $note]);

        return $this->jsonSuccess(['new_balance' => $credits->balanceFor($id)]);
    }
}
