<?php

namespace App\Http\Controllers;

use App\Models\Attendance;
use App\Models\Site;
use App\Models\Staff;
use Illuminate\Http\Request;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\DB;

class AttendanceController extends Controller
{
    public function index(Request $request)
    {
        $this->authorizeManage();

        $sites = Site::orderBy('name')->get();
        $siteId = $request->get('site_id') ?: optional($sites->first())->id;
        $month  = (int) $request->get('month', now()->month);
        $year   = (int) $request->get('year', now()->year);

        $start = Carbon::create($year, $month, 1)->startOfMonth();
        $end   = $start->copy()->endOfMonth();

        // Build the day columns for the month.
        $days = [];
        for ($d = $start->copy(); $d->lte($end); $d->addDay()) {
            $days[] = [
                'day'     => $d->day,
                'date'    => $d->toDateString(),
                'dow'     => $d->format('D'),        // Mon, Tue …
                'weekend' => $d->isWeekend(),
            ];
        }

        // All staff assigned to the site (exclude only resigned) so blank/unverified
        // imported records still appear and can be marked.
        $staff = $siteId
            ? Staff::where('site_id', $siteId)->where('status', '!=', 'resign')->orderBy('name')->get()
            : collect();

        // Existing marks: [staff_id][YYYY-MM-DD] = status
        $marks = [];
        if ($staff->isNotEmpty()) {
            Attendance::whereIn('staff_id', $staff->pluck('id'))
                ->whereBetween('date', [$start->toDateString(), $end->toDateString()])
                ->get(['staff_id', 'date', 'status'])
                ->each(function ($a) use (&$marks) {
                    $marks[$a->staff_id][$a->date->toDateString()] = $a->status;
                });
        }

        return view('attendance.index', compact('sites', 'siteId', 'month', 'year', 'days', 'staff', 'marks'));
    }

    // Per-site totals for a month: one row per site, one column per code.
    public function summary(Request $request)
    {
        $this->authorizeManage();

        $month = (int) $request->get('month', now()->month);
        $year  = (int) $request->get('year', now()->year);
        $start = Carbon::create($year, $month, 1)->startOfMonth();
        $end   = $start->copy()->endOfMonth();

        $codes = array_keys(Attendance::CODES);

        // matrix[site_id][status] = count
        $matrix = [];
        Attendance::whereBetween('date', [$start->toDateString(), $end->toDateString()])
            ->select('site_id', 'status', DB::raw('count(*) as c'))
            ->groupBy('site_id', 'status')
            ->get()
            ->each(function ($r) use (&$matrix) {
                $matrix[$r->site_id][$r->status] = (int) $r->c;
            });

        // Coverage per site: how many staff were marked, and how many distinct days.
        $coverage = Attendance::whereBetween('date', [$start->toDateString(), $end->toDateString()])
            ->select('site_id',
                DB::raw('count(distinct staff_id) as staff_marked'),
                DB::raw('count(distinct date) as days_recorded'))
            ->groupBy('site_id')
            ->get()->keyBy('site_id');

        // staff assigned per site (non-resigned)
        $staffCounts = Staff::where('status', '!=', 'resign')
            ->whereNotNull('site_id')
            ->select('site_id', DB::raw('count(*) as c'))
            ->groupBy('site_id')->pluck('c', 'site_id');

        // Show sites that have staff or have any marks this month.
        $sites = Site::orderBy('name')->get()->filter(function ($site) use ($staffCounts, $matrix) {
            return ($staffCounts[$site->id] ?? 0) > 0 || isset($matrix[$site->id]);
        })->values();

        return view('attendance.summary', compact(
            'sites', 'month', 'year', 'codes', 'matrix', 'staffCounts', 'coverage'
        ));
    }

    public function store(Request $request)
    {
        $this->authorizeManage();

        $data = $request->validate([
            'site_id' => ['required', 'exists:sites,id'],
            'month'   => ['required', 'integer', 'between:1,12'],
            'year'    => ['required', 'integer', 'between:2000,2100'],
            'att'     => ['nullable', 'array'],
        ]);

        $valid = array_keys(Attendance::CODES);
        $today = now()->toDateString();

        DB::transaction(function () use ($request, $data, $valid, $today) {
            foreach ($request->input('att', []) as $staffId => $dates) {
                foreach ($dates as $date => $status) {
                    if ($date > $today) {
                        continue; // never record future dates
                    }
                    $status = strtoupper(trim((string) $status));
                    if ($status === '') {
                        Attendance::where('staff_id', $staffId)->whereDate('date', $date)->delete();
                        continue;
                    }
                    if (! in_array($status, $valid, true)) {
                        continue; // ignore unknown codes
                    }
                    Attendance::updateOrCreate(
                        ['staff_id' => $staffId, 'date' => $date],
                        ['status' => $status, 'site_id' => $data['site_id']]
                    );
                }
            }
        });

        return redirect()
            ->route('attendance.index', ['site_id' => $data['site_id'], 'month' => $data['month'], 'year' => $data['year']])
            ->with('success', 'Attendance saved.');
    }

    /** Admin, finance and supervisor all record and report on attendance. */
    private function authorizeManage(): void
    {
        $u = auth()->user();
        abort_unless($u->isAdmin() || $u->isFinance() || $u->isSupervisor(), 403);
    }
}
