<?php

namespace App\Http\Controllers\Api;

use App\Http\Controllers\Controller;
use App\Http\Resources\AllocationResource;
use App\Models\Allocation;
use App\Services\AllocationMatrixService;
use App\Services\AllocationValidationService;
use App\Services\CapacityService;
use App\Services\VarianceCalculator;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Validator;

/**
 * @group Resource Allocation
 *
 * Endpoints for managing resource allocations.
 */
class AllocationController extends Controller
{
    protected AllocationValidationService $validationService;

    public function __construct(
        AllocationValidationService $validationService,
        protected VarianceCalculator $varianceCalculator,
        protected CapacityService $capacityService,
        protected AllocationMatrixService $allocationMatrixService
    ) {
        $this->validationService = $validationService;
    }

    /**
     * List allocations / Get allocation matrix
     *
     * Get all allocations, optionally filtered by month.
     *
     * @authenticated
     *
     * @queryParam month string Filter by month (YYYY-MM format). Example: 2026-02
     *
     * @response 200 {
     *   "data": [
     *     {
     *       "id": "550e8400-e29b-41d4-a716-446655440000",
     *       "project_id": "550e8400-e29b-41d4-a716-446655440001",
     *       "team_member_id": "550e8400-e29b-41d4-a716-446655440002",
     *       "month": "2026-02",
     *       "allocated_hours": 40.00,
     *       "is_untracked": false,
     *       "row_variance": { "allocated_total": 80, "planned_month": 100, "variance": -20, "status": "UNDER" },
     *       "column_variance": { "allocated": 80, "capacity": 160, "variance": -80, "status": "UNDER" }
     *     }
     *   ]
     * }
     */
    public function index(Request $request): JsonResponse
    {
        $month = $request->query('month');

        $query = Allocation::with(['project', 'teamMember']);

        if ($month) {
            // Convert YYYY-MM to YYYY-MM-01 for date comparison
            $monthDate = $month.'-01';
            $query->where('month', $monthDate);
        }

        $allocations = $query->get();

        // Compute variance indicators for each allocation if month is specified
        if ($month) {
            $allocations->each(function ($allocation) use ($month) {
                // Add untracked flag
                $allocation->is_untracked = $allocation->team_member_id === null;

                // Add row variance (project level)
                $rowVariance = $this->varianceCalculator->calculateRowVariance(
                    $allocation->project_id,
                    $month
                );
                $allocation->row_variance = $rowVariance;

                // Add column variance only for tracked allocations
                if ($allocation->team_member_id !== null) {
                    $columnVariance = $this->varianceCalculator->calculateColumnVariance(
                        $allocation->team_member_id,
                        $month,
                        $this->capacityService
                    );
                    $allocation->column_variance = $columnVariance;
                } else {
                    $allocation->column_variance = null;
                }
            });
        }

        return $this->wrapResource(AllocationResource::collection($allocations));
    }

    /**
     * Create a new allocation
     *
     * Allocate hours for a team member to a project for a specific month.
     *
     * @authenticated
     *
     * @bodyParam project_id string required Project UUID. Example: 550e8400-e29b-41d4-a716-446655440001
     * @bodyParam team_member_id string optional Team member UUID (null for untracked). Example: 550e8400-e29b-41d4-a716-446655440002
     * @bodyParam month string required Month (YYYY-MM format). Example: 2026-02
     * @bodyParam allocated_hours numeric required Hours to allocate (must be >= 0). Example: 40
     *
     * @response 201 {
     *   "data": {
     *     "id": "550e8400-e29b-41d4-a716-446655440000",
     *     "project_id": "550e8400-e29b-41d4-a716-446655440001",
     *     "team_member_id": "550e8400-e29b-41d4-a716-446655440002",
     *     "month": "2026-02",
     *     "allocated_hours": 40.00
     *   }
     * }
     * @response 422 {"message":"Validation failed","errors":{"allocated_hours":["Allocated hours must be greater than or equal to 0"]}}
     */
    public function store(Request $request): JsonResponse
    {
        $validator = Validator::make($request->all(), [
            'project_id' => 'required|uuid|exists:projects,id',
            'team_member_id' => 'nullable|uuid|exists:team_members,id',
            'month' => 'required|date_format:Y-m',
            'allocated_hours' => 'required|numeric|min:0',
        ]);

        if ($validator->fails()) {
            return response()->json([
                'message' => 'Validation failed',
                'errors' => $validator->errors(),
            ], 422);
        }

        // Validate against capacity and approved estimate (skip for untracked)
        $teamMemberId = $request->input('team_member_id');
        $capacityValidation = ['valid' => true, 'warning' => null, 'utilization' => 0];

        if ($teamMemberId) {
            $capacityValidation = $this->validationService->validateCapacity(
                $teamMemberId,
                $request->input('month'),
                (float) $request->input('allocated_hours')
            );
        }

        $estimateValidation = $this->validationService->validateApprovedEstimate(
            $request->input('project_id'),
            $request->input('month'),
            (float) $request->input('allocated_hours')
        );

        // Convert YYYY-MM to YYYY-MM-01 for database storage
        $data = $request->all();
        $data['month'] = $data['month'].'-01';

        $allocation = Allocation::create($data);
        $allocation->load(['project', 'teamMember']);

        $response = new AllocationResource($allocation);
        $responseData = $response->toArray($request);

        // Add variance data
        $month = $request->input('month');
        $responseData['is_untracked'] = $teamMemberId === null;

        // Row variance (project level)
        $rowVariance = $this->varianceCalculator->calculateRowVariance(
            $allocation->project_id,
            $month
        );
        $responseData['row_variance'] = $rowVariance;

        // Column variance (member level) - only for tracked allocations
        if ($teamMemberId) {
            $columnVariance = $this->varianceCalculator->calculateColumnVariance(
                $teamMemberId,
                $month,
                $this->capacityService
            );
            $responseData['column_variance'] = $columnVariance;
            $responseData['utilization'] = $capacityValidation['utilization'];
        } else {
            $responseData['column_variance'] = null;
        }

        // Add validation warnings/info to response
        $responseData['warnings'] = [];
        if ($capacityValidation['warning']) {
            $responseData['warnings'][] = $capacityValidation['warning'];
        }
        if ($estimateValidation['message']) {
            $responseData['warnings'][] = $estimateValidation['message'];
        }

        return response()->json(['data' => $responseData], 201);
    }

    /**
     * Get a single allocation
     *
     * Get details of a specific allocation by ID.
     *
     * @authenticated
     *
     * @urlParam id string required Allocation UUID. Example: 550e8400-e29b-41d4-a716-446655440000
     *
     * @response 200 {
     *   "data": {
     *     "id": "550e8400-e29b-41d4-a716-446655440000",
     *     "project_id": "550e8400-e29b-41d4-a716-446655440001",
     *     "team_member_id": "550e8400-e29b-41d4-a716-446655440002",
     *     "month": "2026-02",
     *     "allocated_hours": 40.00
     *   }
     * }
     * @response 404 {"message": "Allocation not found"}
     */
    public function show(string $id): JsonResponse
    {
        $allocation = Allocation::with(['project', 'teamMember'])->find($id);

        if (! $allocation) {
            return response()->json([
                'message' => 'Allocation not found',
            ], 404);
        }

        return $this->wrapResource(new AllocationResource($allocation));
    }

    /**
     * Update an allocation
     *
     * Update an existing allocation's hours.
     *
     * @authenticated
     *
     * @urlParam id string required Allocation UUID. Example: 550e8400-e29b-41d4-a716-446655440000
     *
     * @bodyParam allocated_hours numeric required Hours to allocate (must be >= 0). Example: 60
     *
     * @response 200 {
     *   "data": {
     *     "id": "550e8400-e29b-41d4-a716-446655440000",
     *     "project_id": "550e8400-e29b-41d4-a716-446655440001",
     *     "team_member_id": "550e8400-e29b-41d4-a716-446655440002",
     *     "month": "2026-02",
     *     "allocated_hours": 60.00
     *   }
     * }
     * @response 404 {"message": "Allocation not found"}
     * @response 422 {"message":"Validation failed","errors":{"allocated_hours":["Allocated hours must be greater than or equal to 0"]}}
     */
    public function update(Request $request, string $id): JsonResponse
    {
        $allocation = Allocation::find($id);

        if (! $allocation) {
            return response()->json([
                'message' => 'Allocation not found',
            ], 404);
        }

        $validator = Validator::make($request->all(), [
            'allocated_hours' => 'required|numeric|min:0',
        ]);

        if ($validator->fails()) {
            return response()->json([
                'message' => 'Validation failed',
                'errors' => $validator->errors(),
            ], 422);
        }

        $allocation->update($request->all());
        $allocation->load(['project', 'teamMember']);

        return $this->wrapResource(new AllocationResource($allocation));
    }

    /**
     * Delete an allocation
     *
     * Remove an allocation.
     *
     * @authenticated
     *
     * @urlParam id string required Allocation UUID. Example: 550e8400-e29b-41d4-a716-446655440000
     *
     * @response 200 {"message": "Allocation deleted successfully"}
     * @response 404 {"message": "Allocation not found"}
     */
    public function destroy(string $id): JsonResponse
    {
        $allocation = Allocation::find($id);

        if (! $allocation) {
            return response()->json([
                'message' => 'Allocation not found',
            ], 404);
        }

        $allocation->delete();

        return response()->json([
            'message' => 'Allocation deleted successfully',
        ]);
    }

    /**
     * Bulk create allocations
     *
     * Create multiple allocations in a single request.
     * Supports partial success - valid items are created, invalid items are reported.
     *
     * @authenticated
     *
     * @bodyParam allocations array required Array of allocations. Example: [{"project_id": "...", "team_member_id": "...", "month": "2026-02", "allocated_hours": 40}]
     *
     * @response 201 {
     *   "data": [
     *     { "index": 0, "id": "...", "status": "created" }
     *   ],
     *   "failed": [
     *     { "index": 1, "errors": { "allocated_hours": ["..."] } }
     *   ],
     *   "summary": { "created": 1, "failed": 1 }
     * }
     */
    public function bulkStore(Request $request): JsonResponse
    {
        // Basic validation only - individual item validation happens in the loop
        // This allows partial success even if some items have invalid data
        $validator = Validator::make($request->all(), [
            'allocations' => 'required|array|min:1',
            'allocations.*.project_id' => 'required',
            'allocations.*.month' => 'required',
            'allocations.*.allocated_hours' => 'required|numeric',
        ]);

        if ($validator->fails()) {
            return response()->json([
                'message' => 'Validation failed',
                'errors' => $validator->errors(),
            ], 422);
        }

        $data = [];
        $failed = [];
        $created = 0;
        $failedCount = 0;

        foreach ($request->input('allocations') as $index => $allocationData) {
            // Convert YYYY-MM to YYYY-MM-01 for database storage
            $allocationData['month'] = $allocationData['month'].'-01';

            // Validate each item individually (for partial bulk success)
            $itemValidator = Validator::make($allocationData, [
                'project_id' => 'required|uuid|exists:projects,id',
                'team_member_id' => 'nullable|uuid|exists:team_members,id',
                'month' => 'required|date',
                'allocated_hours' => 'required|numeric|min:0',
            ]);

            if ($itemValidator->fails()) {
                $failed[] = [
                    'index' => $index,
                    'errors' => $itemValidator->errors()->toArray(),
                ];
                $failedCount++;

                continue;
            }

            try {
                $allocation = Allocation::create($allocationData);
                $data[] = [
                    'index' => $index,
                    'id' => $allocation->id,
                    'status' => 'created',
                ];
                $created++;
            } catch (\Exception $e) {
                $failed[] = [
                    'index' => $index,
                    'errors' => ['allocation' => ['Failed to create allocation: '.$e->getMessage()]],
                ];
                $failedCount++;
            }
        }

        return response()->json([
            'data' => $data,
            'failed' => $failed,
            'summary' => [
                'created' => $created,
                'failed' => $failedCount,
            ],
        ], 201);
    }
}
