<?php

namespace App\Modules\HR\Reports\Repositories;

use App\Modules\HR\Reports\Support\HrReportKey;
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
use Illuminate\Database\Query\Builder;
use Illuminate\Support\Facades\DB;

class OperationsReportRepository
{
    /** @param array<string, mixed> $filters */
    public function paginate(int $tenantId, HrReportKey $report, array $filters): LengthAwarePaginator
    {
        $query = match ($report->category()) {
            'attendance' => $this->attendance($tenantId, $report),
            'leave' => $this->leave($tenantId, $report),
            'payroll' => $this->payroll($tenantId, $report),
            'assets' => $this->assets($tenantId, $report),
        };
        $this->filter($query, $filters, $report);

        return $query->paginate((int) ($filters['per_page'] ?? 25))->withQueryString();
    }

    private function attendance(int $tenantId, HrReportKey $report): Builder
    {
        $query = DB::table('attendance_records as r')
            ->join('employees as e', 'e.id', '=', 'r.employee_id')
            ->leftJoin('departments as d', 'd.id', '=', 'e.department_id')
            ->where('r.tenant_id', $tenantId)
            ->select('r.attendance_date', 'e.employee_code', 'e.full_name', 'd.name as department', 'r.check_in_at', 'r.check_out_at', 'r.status', 'r.overtime_minutes');

        return match ($report) {
            HrReportKey::ATTENDANCE_LATE => $query->where('r.status', 'late'),
            HrReportKey::ATTENDANCE_MISSING => $query->where('r.missing_checkout', true),
            HrReportKey::ATTENDANCE_OVERTIME => $query->where('r.overtime_minutes', '>', 0),
            default => $query,
        };
    }

    private function leave(int $tenantId, HrReportKey $report): Builder
    {
        if ($report === HrReportKey::LEAVE_HISTORY) {
            return DB::table('leave_requests as r')
                ->join('employees as e', 'e.id', '=', 'r.employee_id')
                ->leftJoin('departments as d', 'd.id', '=', 'e.department_id')
                ->leftJoin('leave_types as t', 't.id', '=', 'r.leave_type_id')
                ->where('r.tenant_id', $tenantId)
                ->select('e.employee_code', 'e.full_name', 'd.name as department', 't.name as leave_type', 'r.start_date', 'r.end_date', 'r.total_days', 'r.status');
        }

        $query = DB::table('leave_balances as b')
            ->join('employees as e', 'e.id', '=', 'b.employee_id')
            ->leftJoin('departments as d', 'd.id', '=', 'e.department_id')
            ->leftJoin('leave_types as t', 't.id', '=', 'b.leave_type_id')
            ->where('b.tenant_id', $tenantId)
            ->select('e.employee_code', 'e.full_name', 'd.name as department', 't.name as leave_type', 'b.year', 'b.entitled_days', 'b.used_days', 'b.carried_forward_days')
            ->selectRaw('(b.entitled_days + b.carried_forward_days - b.used_days) as remaining_days');

        if ($report === HrReportKey::LEAVE_LIABILITY) {
            $query->addSelect('e.salary')->selectRaw(
                'ROUND((b.entitled_days + b.carried_forward_days - b.used_days) * e.salary / 30, 2) as estimated_liability'
            );
        }

        return $query;
    }

    private function payroll(int $tenantId, HrReportKey $report): Builder
    {
        $query = DB::table('payroll_employee_snapshots as s')
            ->join('payroll_periods as p', 'p.id', '=', 's.payroll_period_id')
            ->join('employees as e', 'e.id', '=', 's.employee_id')
            ->leftJoin('departments as d', 'd.id', '=', 'e.department_id')
            ->leftJoin('hr_cost_centers as c', 'c.id', '=', 'e.cost_center_id')
            ->where('s.tenant_id', $tenantId);

        return match ($report) {
            HrReportKey::PAYROLL_SUMMARY => $query
                ->select('p.name as period')->selectRaw('COUNT(s.id) as employees, SUM(s.gross_amount) as gross_amount')
                ->groupBy('p.id', 'p.name')->latest('p.id'),
            HrReportKey::PAYROLL_COST_CENTER => $query
                ->select('p.name as period', 'c.name as cost_center')
                ->selectRaw('COUNT(s.id) as employees, SUM(s.gross_amount) as gross_amount')
                ->groupBy('p.id', 'p.name', 'c.id', 'c.name')->latest('p.id'),
            default => $query
                ->select('p.name as period', 'e.employee_code', 'e.full_name', 'd.name as department', 's.base_salary', 's.adjustments_total', 's.gross_amount')
                ->latest('p.id'),
        };
    }

    private function assets(int $tenantId, HrReportKey $report): Builder
    {
        $query = DB::table('hr_assets as a')
            ->leftJoin('employees as e', 'e.id', '=', 'a.current_employee_id')
            ->leftJoin('departments as d', 'd.id', '=', 'e.department_id')
            ->where('a.tenant_id', $tenantId)
            ->select('a.asset_code', 'a.name', 'a.category', 'a.serial_number', 'a.status', 'e.full_name as employee', 'd.name as department');

        return $report === HrReportKey::ASSETS_ASSIGNED
            ? $query->where('a.status', 'assigned')
            : $query->whereIn('a.status', ['damaged', 'lost']);
    }

    /** @param array<string, mixed> $filters */
    private function filter(Builder $query, array $filters, HrReportKey $report): void
    {
        if (! empty($filters['department_id']) && $report->category() !== 'assets') {
            $query->where('e.department_id', $filters['department_id']);
        }
        $date = match ($report->category()) {
            'attendance' => 'r.attendance_date',
            'leave' => $report === HrReportKey::LEAVE_HISTORY ? 'r.start_date' : null,
            'payroll' => 'p.start_date',
            default => 'a.created_at',
        };
        if ($date && ! empty($filters['date_from'])) {
            $query->whereDate($date, '>=', $filters['date_from']);
        }
        if ($date && ! empty($filters['date_to'])) {
            $query->whereDate($date, '<=', $filters['date_to']);
        }
    }
}
