<?php

namespace App\Http\Controllers\Web;

use App\Http\Controllers\Controller;
use App\Models\Department;
use App\Models\Location;
use App\Models\Role;
use App\Models\User;
use App\Models\WeekoffRule;
use App\Models\WeekoffRuleAssignment;
use Carbon\Carbon;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Validation\Rule;

class WeekoffRuleController extends Controller
{
    public function index(Request $request)
    {
        $companyId = (int) (Auth::user()?->company_id ?? 0);
        $tab = $request->get('tab', 'rules') === 'assign' ? 'assign' : 'rules';

        $rules = WeekoffRule::query()
            ->when($companyId > 0, fn ($q) => $q->where('company_id', $companyId))
            ->orderByDesc('is_company_default')
            ->orderBy('name')
            ->get();

        $roles = Role::query()
            ->when($companyId > 0, fn ($q) => $q->where('company_id', $companyId))
            ->orderBy('display_name')
            ->get();

        $locations = Location::query()
            ->when($companyId > 0, fn ($q) => $q->where('company_id', $companyId))
            ->orderBy('name')
            ->get();

        $employeesQuery = User::query()
            ->with(['role', 'department', 'location'])
            ->forActiveDirectory()
            ->when($companyId > 0, fn ($q) => $q->where('company_id', $companyId))
            ->orderBy('name');

        if ($request->filled('role_id')) {
            $employeesQuery->where('role_id', (int) $request->integer('role_id'));
        }
        if ($request->filled('location_id')) {
            $employeesQuery->where('location_id', (int) $request->integer('location_id'));
        }

        $employees = $employeesQuery->get();

        $activeRules = $rules->where('status', WeekoffRule::STATUS_ACTIVE)->values();

        $currentAssignments = WeekoffRuleAssignment::query()
            ->with('rule')
            ->when($companyId > 0, fn ($q) => $q->where('company_id', $companyId))
            ->orderByDesc('effective_from')
            ->orderByDesc('id')
            ->get()
            ->groupBy('user_id')
            ->map(fn ($items) => $items->first());

        $editRule = null;
        if ($request->filled('edit')) {
            $editRule = $rules->firstWhere('id', (int) $request->integer('edit'));
        }

        return view('weekoff-rules.index', compact(
            'tab',
            'rules',
            'activeRules',
            'roles',
            'locations',
            'employees',
            'currentAssignments',
            'editRule'
        ));
    }

    public function store(Request $request)
    {
        $companyId = (int) (Auth::user()?->company_id ?? 0);
        $validated = $this->validateRule($request);

        $schedule = $this->normalizeSchedule($request->input('schedule', []));
        $isDefault = $request->boolean('is_company_default');

        if ($isDefault) {
            $this->clearCompanyDefault($companyId);
        }

        WeekoffRule::create([
            'company_id' => $companyId,
            'name' => $validated['name'],
            'description' => $validated['description'] ?? null,
            'effective_from' => $validated['effective_from'],
            'status' => $validated['status'],
            'half_day_enabled' => $request->boolean('half_day_enabled'),
            'is_company_default' => $isDefault,
            'schedule' => $schedule,
        ]);

        return redirect()
            ->route('weekoff-rules.index', ['tab' => 'rules'])
            ->with('success', 'Weekoff rule created successfully.');
    }

    public function update(Request $request, WeekoffRule $weekoffRule)
    {
        $this->authorizeRule($weekoffRule);
        $companyId = (int) (Auth::user()?->company_id ?? 0);
        $validated = $this->validateRule($request, $weekoffRule);

        $schedule = $this->normalizeSchedule($request->input('schedule', []));
        $isDefault = $request->boolean('is_company_default');

        if ($isDefault) {
            $this->clearCompanyDefault($companyId, (int) $weekoffRule->id);
        }

        $weekoffRule->update([
            'name' => $validated['name'],
            'description' => $validated['description'] ?? null,
            'effective_from' => $validated['effective_from'],
            'status' => $validated['status'],
            'half_day_enabled' => $request->boolean('half_day_enabled'),
            'is_company_default' => $isDefault,
            'schedule' => $schedule,
        ]);

        return redirect()
            ->route('weekoff-rules.index', ['tab' => 'rules'])
            ->with('success', 'Weekoff rule updated successfully.');
    }

    public function assign(Request $request)
    {
        $companyId = (int) (Auth::user()?->company_id ?? 0);
        $validated = $request->validate([
            'weekoff_rule_id' => [
                'required',
                'integer',
                Rule::exists('weekoff_rules', 'id')->where(fn ($q) => $q->where('company_id', $companyId)),
            ],
            'effective_from' => 'required|date',
            'user_ids' => 'required|array|min:1',
            'user_ids.*' => 'integer',
        ]);

        $rule = WeekoffRule::query()->findOrFail((int) $validated['weekoff_rule_id']);
        $this->authorizeRule($rule);

        $userIds = User::query()
            ->forActiveDirectory()
            ->when($companyId > 0, fn ($q) => $q->where('company_id', $companyId))
            ->whereIn('id', $validated['user_ids'])
            ->pluck('id')
            ->all();

        if ($userIds === []) {
            return back()->with('error', 'No valid employees selected.');
        }

        $effectiveFrom = Carbon::parse($validated['effective_from'])->toDateString();
        $assignedBy = (int) (Auth::id() ?? 0);

        foreach ($userIds as $userId) {
            WeekoffRuleAssignment::create([
                'company_id' => $companyId,
                'weekoff_rule_id' => $rule->id,
                'user_id' => $userId,
                'effective_from' => $effectiveFrom,
                'assigned_by' => $assignedBy > 0 ? $assignedBy : null,
            ]);
        }

        return redirect()
            ->route('weekoff-rules.index', ['tab' => 'assign'])
            ->with('success', count($userIds).' employee(s) assigned to '.$rule->name.'.');
    }

    public function history(User $user)
    {
        $companyId = (int) (Auth::user()?->company_id ?? 0);
        abort_unless((int) $user->company_id === $companyId, 403);

        $assignments = WeekoffRuleAssignment::query()
            ->with(['rule', 'assignedByUser'])
            ->where('company_id', $companyId)
            ->where('user_id', $user->id)
            ->orderByDesc('effective_from')
            ->orderByDesc('id')
            ->get();

        return view('weekoff-rules.history', compact('user', 'assignments'));
    }

    /**
     * @return array<string, mixed>
     */
    protected function validateRule(Request $request, ?WeekoffRule $existing = null): array
    {
        $companyId = (int) (Auth::user()?->company_id ?? 0);

        return $request->validate([
            'name' => 'required|string|max:191',
            'description' => 'nullable|string|max:2000',
            'effective_from' => 'required|date',
            'status' => ['required', Rule::in([WeekoffRule::STATUS_ACTIVE, WeekoffRule::STATUS_INACTIVE])],
            'is_company_default' => 'nullable|boolean',
            'half_day_enabled' => 'nullable|boolean',
            'schedule' => 'nullable|array',
        ]);
    }

    /**
     * @param  array<string, mixed>  $raw
     * @return array<string, array<string, string>>
     */
    protected function normalizeSchedule(array $raw): array
    {
        $schedule = WeekoffRule::defaultSchedule();
        $allowed = [WeekoffRule::DAY_WORKING, WeekoffRule::DAY_HALF, WeekoffRule::DAY_WEEKLY_OFF];

        foreach (WeekoffRule::weekKeys() as $week) {
            foreach (WeekoffRule::dayKeys() as $day) {
                $value = $raw[$week][$day] ?? $schedule[$week][$day];
                $schedule[$week][$day] = in_array($value, $allowed, true)
                    ? $value
                    : WeekoffRule::DAY_WORKING;
            }
        }

        return $schedule;
    }

    protected function clearCompanyDefault(int $companyId, ?int $exceptId = null): void
    {
        $query = WeekoffRule::query()
            ->where('company_id', $companyId)
            ->where('is_company_default', true);

        if ($exceptId) {
            $query->where('id', '!=', $exceptId);
        }

        $query->update(['is_company_default' => false]);
    }

    protected function authorizeRule(WeekoffRule $rule): void
    {
        abort_unless((int) $rule->company_id === (int) (Auth::user()?->company_id ?? 0), 403);
    }
}
