<?php

namespace App\Services;

use App\Models\Allocation;
use App\Models\ProjectMonthPlan;
use Carbon\Carbon;

class VarianceCalculator
{
    /**
     * Calculate row variance for a project in a given month.
     * Row variance = allocated_total - planned_month
     *
     * @return array{
     *   allocated_total: float,
     *   planned_month: float,
     *   variance: float,
     *   status: string
     * }
     */
    public function calculateRowVariance(string $projectId, string $month): array
    {
        // Convert YYYY-MM to YYYY-MM-01 and then to a Carbon date for proper comparison
        $monthDate = strlen($month) === 7 ? Carbon::createFromFormat('Y-m', $month)->startOfMonth() : Carbon::parse($month);

        // Get total allocated hours for this project/month (including untracked)
        $allocatedTotal = Allocation::where('project_id', $projectId)
            ->whereMonth('month', $monthDate->month)
            ->whereYear('month', $monthDate->year)
            ->sum('allocated_hours');

        // Get planned hours for this project/month (treat null as 0)
        $plannedMonth = $this->getPlannedHoursForMonth($projectId, $month);

        $variance = $allocatedTotal - $plannedMonth;

        return [
            'allocated_total' => $allocatedTotal,
            'planned_month' => $plannedMonth,
            'variance' => $variance,
            'status' => $this->determineStatus($variance),
        ];
    }

    /**
     * Calculate column variance for a team member in a given month.
     * Column variance = member_allocated - member_capacity
     *
     * @return array{
     *   allocated: float,
     *   capacity: float,
     *   variance: float,
     *   status: string
     * }
     */
    public function calculateColumnVariance(string $teamMemberId, string $month, CapacityService $capacityService): array
    {
        // Convert YYYY-MM to Carbon date for proper comparison
        $monthDate = strlen($month) === 7 ? Carbon::createFromFormat('Y-m', $month)->startOfMonth() : Carbon::parse($month);

        // Get total allocated hours for this member/month (excluding untracked)
        $allocated = Allocation::where('team_member_id', $teamMemberId)
            ->whereMonth('month', $monthDate->month)
            ->whereYear('month', $monthDate->year)
            ->sum('allocated_hours');

        // Get member capacity
        $capacityData = $capacityService->calculateIndividualCapacity($teamMemberId, $month);
        $capacity = $capacityData['hours'] ?? 0;

        $variance = $allocated - $capacity;

        return [
            'allocated' => $allocated,
            'capacity' => $capacity,
            'variance' => $variance,
            'status' => $this->determineStatus($variance),
        ];
    }

    /**
     * Get planned hours for a project/month.
     * Returns 0 if no plan exists (blank month treated as 0).
     */
    public function getPlannedHoursForMonth(string $projectId, string $month): float
    {
        $monthDate = strlen($month) === 7 ? Carbon::createFromFormat('Y-m', $month)->startOfMonth() : Carbon::parse($month);

        $plan = ProjectMonthPlan::where('project_id', $projectId)
            ->whereMonth('month', $monthDate->month)
            ->whereYear('month', $monthDate->year)
            ->first();

        // Blank plan is treated as 0 for allocation variance
        return (float) ($plan?->planned_hours ?? 0);
    }

    /**
     * Determine status based on variance value.
     *
     * - Positive variance (> 0): OVER (red)
     * - Negative variance (< 0): UNDER (amber)
     * - Zero variance: MATCH (neutral)
     */
    public function determineStatus(float $variance): string
    {
        $epsilon = 0.0001;

        if ($variance > $epsilon) {
            return 'OVER';
        }

        if ($variance < -$epsilon) {
            return 'UNDER';
        }

        return 'MATCH';
    }

    /**
     * Calculate both row and column variances for a complete matrix view.
     *
     * @return array{
     *   project_variances: array<string, array>,
     *   team_member_variances: array<string, array>
     * }
     */
    public function calculateMatrixVariances(string $month, CapacityService $capacityService): array
    {
        // Convert YYYY-MM to Carbon date for proper comparison
        $monthDate = strlen($month) === 7 ? Carbon::createFromFormat('Y-m', $month)->startOfMonth() : Carbon::parse($month);

        // Get all allocations for the month using whereMonth/whereYear
        $allocations = Allocation::whereMonth('month', $monthDate->month)
            ->whereYear('month', $monthDate->year)
            ->get();

        // Calculate row variances per project
        $projectIds = $allocations->pluck('project_id')->unique()->toArray();
        $projectVariances = [];

        foreach ($projectIds as $projectId) {
            $projectVariances[$projectId] = $this->calculateRowVariance($projectId, $month);
        }

        // Calculate column variances per team member (excluding null/untracked)
        $teamMemberIds = $allocations->pluck('team_member_id')
            ->filter(fn ($id) => $id !== null)
            ->unique()
            ->toArray();

        $teamMemberVariances = [];

        foreach ($teamMemberIds as $teamMemberId) {
            $teamMemberVariances[$teamMemberId] = $this->calculateColumnVariance($teamMemberId, $month, $capacityService);
        }

        return [
            'project_variances' => $projectVariances,
            'team_member_variances' => $teamMemberVariances,
        ];
    }
}
