<?php

namespace App\Http\Controllers\Api\V1\Reporting;

use App\Domain\Agents\Enums\AgentStatus;
use App\Domain\Agents\Models\Agent;
use App\Domain\Applicants\Models\Applicant;
use App\Domain\Applicants\Policies\ApplicantPolicy;
use App\Domain\Audit\Models\Activity;
use App\Domain\Billing\Models\Invoice;
use App\Domain\Commissions\Models\Commission;
use App\Domain\Documents\Models\Document;
use App\Domain\Expenses\Models\Expense;
use App\Domain\Expenses\Models\VendorBill;
use App\Domain\Identity\Models\User;
use App\Domain\Identity\Services\ScopeGuard;
use App\Domain\Payments\Models\Payment;
use App\Domain\Recruitment\Enums\JobDemandStatus;
use App\Domain\Recruitment\Models\JobDemand;
use App\Domain\Refunds\Models\Refund;
use App\Domain\Support\Models\SupportTicket;
use App\Domain\Website\Enums\RequisitionStatus;
use App\Domain\Website\Models\EmployerRequisition;
use App\Domain\Workflow\Models\Appointment;
use App\Domain\Workflow\Models\Task;
use App\Http\Controllers\Controller;
use App\Http\Resources\ActivityResource;
use App\Support\ApiResponse;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;

/**
 * The operational dashboard (§60).
 *
 * Every number respects the reader's permission scope, and anything they cannot
 * see is omitted rather than shown as zero — a blank card and a genuine zero
 * mean different things.
 */
class DashboardController extends Controller
{
    public function __construct(private readonly ScopeGuard $scopes) {}

    public function __invoke(Request $request): JsonResponse
    {
        $user = $request->user();

        return ApiResponse::success([
            'applicants' => $user->can('applicant.view') ? $this->applicants($user) : null,
            'documents' => $user->can('document.view') ? $this->documents($user) : null,
            'recruitment' => $user->can('job_demand.view') ? $this->recruitment($user) : null,
            'agents' => $user->can('agent.view') ? $this->agents($user) : null,
            'requisitions' => $user->can('requisition.view') ? $this->requisitions($user) : null,
            'tasks' => $user->can('task.view') ? $this->tasks($user) : null,
            'attention' => $this->attention($user),
            'recent_activity' => $user->can('audit.view')
                ? ActivityResource::collection(
                    Activity::where('company_id', $user->company_id)
                        ->latest('created_at')
                        ->latest('id')
                        ->limit(8)
                        ->get()
                )
                : null,
        ]);
    }

    /** @return array<string, mixed> */
    private function applicants(User $user): array
    {
        $base = fn () => $this->scopes->apply(
            Applicant::query(),
            $user,
            'applicant.view',
            ApplicantPolicy::OWNER_COLUMNS,
        );

        return [
            'total' => $base()->count(),
            'active' => $base()->active()->count(),
            'unverified' => $base()->whereNull('verified_at')->count(),
            'registered_this_month' => $base()->where('created_at', '>=', now()->startOfMonth())->count(),
            // The stage distribution is what a manager actually looks at.
            'by_stage' => $base()
                ->whereNotNull('current_stage_id')
                ->join('pipeline_stages', 'pipeline_stages.id', '=', 'applicants.current_stage_id')
                ->selectRaw('pipeline_stages.name as stage, pipeline_stages.sequence, count(*) as total')
                ->groupBy('pipeline_stages.name', 'pipeline_stages.sequence')
                ->orderBy('pipeline_stages.sequence')
                ->get()
                ->map(fn ($row) => ['stage' => $row->stage, 'total' => (int) $row->total]),
        ];
    }

    /** @return array<string, mixed> */
    private function documents(User $user): array
    {
        $base = fn () => Document::query()
            ->current()
            ->where('company_id', $user->company_id);

        return [
            'awaiting_review' => $base()->awaitingReview()->count(),
            'expiring_soon' => $base()
                ->whereNotNull('expiry_date')
                ->whereBetween('expiry_date', [now()->toDateString(), now()->addDays(30)->toDateString()])
                ->count(),
            'expired' => $base()
                ->whereNotNull('expiry_date')
                ->whereDate('expiry_date', '<', now()->toDateString())
                ->count(),
        ];
    }

    /** @return array<string, mixed> */
    private function recruitment(User $user): array
    {
        $base = fn () => JobDemand::query()->where('company_id', $user->company_id);

        $open = $base()->where('status', JobDemandStatus::Open->value);

        return [
            'open_demands' => (clone $open)->count(),
            'published' => $base()->publiclyVisible()->count(),
            'positions_open' => (int) (clone $open)->sum('quantity_required'),
            'positions_deployed' => (int) (clone $open)->sum('deployed_count'),
        ];
    }

    /** @return array<string, mixed> */
    private function agents(User $user): array
    {
        $base = fn () => $this->scopes->apply(Agent::query(), $user, 'agent.view');

        return [
            'active' => $base()->where('status', AgentStatus::Active->value)->count(),
            'pending_approval' => $base()->where('status', AgentStatus::PendingApproval->value)->count(),
        ];
    }

    /** @return array<string, mixed> */
    private function requisitions(User $user): array
    {
        $base = fn () => EmployerRequisition::query()->where('company_id', $user->company_id);

        return [
            'open' => $base()->open()->count(),
            'awaiting_review' => $base()->where('status', RequisitionStatus::Submitted->value)->count(),
            'approved' => $base()->where('status', RequisitionStatus::Approved->value)->count(),
        ];
    }

    /** @return array<string, mixed> */
    private function tasks(User $user): array
    {
        $mine = fn () => Task::query()
            ->where('company_id', $user->company_id)
            ->where('assigned_to', $user->getKey());

        return [
            'assigned_to_me' => $mine()->open()->count(),
            'due_today' => $mine()->open()->whereDate('due_at', now()->toDateString())->count(),
            'overdue' => $mine()->overdue()->count(),
        ];
    }

    /**
     * The "what needs doing" list — each item links somewhere actionable, and
     * only appears when the reader can act on it.
     *
     * @return array<int, array<string, mixed>>
     */
    private function attention(User $user): array
    {
        $items = [];

        if ($user->can('document.verify')) {
            $count = Document::current()
                ->where('company_id', $user->company_id)
                ->awaitingReview()
                ->count();

            if ($count > 0) {
                $items[] = [
                    'label' => "{$count} document".($count === 1 ? '' : 's').' awaiting verification',
                    'href' => '/app/documents',
                    'tone' => 'info',
                ];
            }
        }

        if ($user->can('applicant.view')) {
            $count = $this->scopes
                ->apply(Applicant::query(), $user, 'applicant.view', ApplicantPolicy::OWNER_COLUMNS)
                ->whereNull('verified_at')
                ->active()
                ->count();

            if ($count > 0) {
                $items[] = [
                    'label' => "{$count} applicant".($count === 1 ? '' : 's').' awaiting verification',
                    'href' => '/app/applicants?unverified=1',
                    'tone' => 'warning',
                ];
            }
        }

        if ($user->can('requisition.view')) {
            $count = EmployerRequisition::where('company_id', $user->company_id)
                ->where('status', RequisitionStatus::Submitted->value)
                ->count();

            if ($count > 0) {
                $items[] = [
                    'label' => "{$count} employer requisition".($count === 1 ? '' : 's').' to review',
                    'href' => '/app/requisitions',
                    'tone' => 'warning',
                ];
            }
        }

        if ($user->can('agent.approve')) {
            $count = $this->scopes
                ->apply(Agent::query(), $user, 'agent.view')
                ->where('status', AgentStatus::PendingApproval->value)
                ->count();

            if ($count > 0) {
                $items[] = [
                    'label' => "{$count} agent".($count === 1 ? '' : 's').' awaiting approval',
                    'href' => '/app/agents?status=pending_approval',
                    'tone' => 'info',
                ];
            }
        }

        if ($user->can('payment.confirm')) {
            $count = Payment::where('company_id', $user->company_id)
                ->awaitingVerification()
                ->count();

            if ($count > 0) {
                $items[] = [
                    'label' => "{$count} deposit".($count === 1 ? '' : 's').' awaiting verification',
                    'href' => '/app/collections',
                    'tone' => 'warning',
                ];
            }
        }

        if ($user->can('billing.view')) {
            $overdue = Invoice::where('company_id', $user->company_id)
                ->outstanding()
                ->whereNotNull('due_on')
                ->whereDate('due_on', '<', now()->toDateString())
                ->count();

            if ($overdue > 0) {
                $items[] = [
                    'label' => "{$overdue} invoice".($overdue === 1 ? ' is' : 's are').' overdue',
                    'href' => '/app/collections',
                    'tone' => 'danger',
                ];
            }
        }

        if ($user->can('refund.review') || $user->can('refund.approve')) {
            $count = Refund::where('company_id', $user->company_id)->awaitingApproval()->count();

            if ($count > 0) {
                $items[] = [
                    'label' => "{$count} refund request".($count === 1 ? '' : 's').' to decide',
                    'href' => '/app/collections?tab=refunds',
                    'tone' => 'warning',
                ];
            }
        }

        if ($user->can('refund.pay')) {
            $count = Refund::where('company_id', $user->company_id)->awaitingPayout()->count();

            if ($count > 0) {
                $items[] = [
                    'label' => "{$count} approved refund".($count === 1 ? '' : 's').' to pay out',
                    'href' => '/app/collections?tab=refunds',
                    'tone' => 'info',
                ];
            }
        }

        if ($user->can('commission.approve')) {
            $count = Commission::where('company_id', $user->company_id)->awaitingApproval()->count();

            if ($count > 0) {
                $items[] = [
                    'label' => "{$count} commission".($count === 1 ? '' : 's').' awaiting approval',
                    'href' => '/app/commissions',
                    'tone' => 'info',
                ];
            }
        }

        if ($user->can('expense.approve')) {
            $count = Expense::where('company_id', $user->company_id)->awaitingApproval()->count();

            if ($count > 0) {
                $items[] = [
                    'label' => "{$count} expense".($count === 1 ? '' : 's').' awaiting approval',
                    'href' => '/app/expenses',
                    'tone' => 'warning',
                ];
            }
        }

        if ($user->can('vendor.pay')) {
            $overdueBills = VendorBill::where('company_id', $user->company_id)
                ->outstanding()
                ->whereNotNull('due_date')
                ->whereDate('due_date', '<', now()->toDateString())
                ->count();

            if ($overdueBills > 0) {
                $items[] = [
                    'label' => "{$overdueBills} vendor bill".($overdueBills === 1 ? ' is' : 's are').' overdue',
                    'href' => '/app/expenses?tab=bills',
                    'tone' => 'danger',
                ];
            }
        }

        if ($user->can('support.view')) {
            $overdue = SupportTicket::where('company_id', $user->company_id)->overdue()->count();

            if ($overdue > 0) {
                $items[] = [
                    'label' => "{$overdue} support ticket".($overdue === 1 ? ' has' : 's have').
                        ' gone unanswered past its SLA',
                    'href' => '/app/support?overdue=1',
                    'tone' => 'danger',
                ];
            }

            $unassigned = SupportTicket::where('company_id', $user->company_id)
                ->open()
                ->whereNull('assigned_to')
                ->count();

            if ($unassigned > 0) {
                $items[] = [
                    'label' => "{$unassigned} support ticket".($unassigned === 1 ? '' : 's').' need assigning',
                    'href' => '/app/support',
                    'tone' => 'warning',
                ];
            }
        }

        if ($user->can('task.view')) {
            $overdue = Task::query()
                ->where('company_id', $user->company_id)
                ->where('assigned_to', $user->getKey())
                ->overdue()
                ->count();

            if ($overdue > 0) {
                $items[] = [
                    'label' => "{$overdue} of your task".($overdue === 1 ? ' is' : 's are').' overdue',
                    'href' => '/app/tasks?overdue=1',
                    'tone' => 'danger',
                ];
            }

            // An appointment nobody marked attended or missed is a gap in the
            // record, not just a stale row.
            $missing = Appointment::query()
                ->where('company_id', $user->company_id)
                ->awaitingOutcome()
                ->count();

            if ($missing > 0) {
                $items[] = [
                    'label' => "{$missing} appointment".($missing === 1 ? '' : 's').' need an outcome recorded',
                    'href' => '/app/appointments?awaiting_outcome=1',
                    'tone' => 'warning',
                ];
            }
        }

        if ($user->can('document.view')) {
            $count = Document::current()
                ->where('company_id', $user->company_id)
                ->whereNotNull('expiry_date')
                ->whereDate('expiry_date', '<', now()->toDateString())
                ->count();

            if ($count > 0) {
                $items[] = [
                    'label' => "{$count} document".($count === 1 ? '' : 's').' expired',
                    'href' => '/app/documents?expired=1',
                    'tone' => 'danger',
                ];
            }
        }

        return $items;
    }
}
