<?php

namespace App\Http\Controllers\Backend\Payroll;

use App\Exports\BankFileExport;
use App\Http\Controllers\Controller;
use App\Models\Finance\CompanyBankAccount;
use Barryvdh\DomPDF\Facade\Pdf;
use Brian2694\Toastr\Facades\Toastr;
use Carbon\Carbon;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Maatwebsite\Excel\Facades\Excel;

/**
 * Punto 2 — Generación de archivo para banco.
 *
 * Selecciona una quincena calculada, una cuenta empresarial (de dónde sale
 * el dinero) y un banco destino (en qué banco cobran los empleados) y emite
 * un PDF o Excel con la nómina lista para enviar al banco.
 */
class BankFileController extends Controller
{
    /**
     * Pantalla de generación del archivo para banco.
     */
    public function index()
    {
        $companyId = auth()->user()->company_id ?? 1;

        $accounts = CompanyBankAccount::forCurrentCompany()
            ->where('status_id', 1)
            ->orderBy('bank_name')
            ->get();

        // La cuenta bancaria del empleado se guarda en users.bank_name/users.bank_account
        // (configurable desde Empleados → Editar empleado → pestaña "General").
        $banks = DB::table('users')
            ->where('company_id', $companyId)
            ->whereNull('deleted_at')
            ->whereNotNull('bank_name')
            ->where('bank_name', '!=', '')
            ->distinct()
            ->orderBy('bank_name')
            ->pluck('bank_name');

        $dates = DB::table('salary_generates')
            ->where('company_id', $companyId)
            ->where('is_calculated', 1)
            ->distinct()
            ->orderBy('date', 'desc')
            ->pluck('date');

        $data = [
            'title' => 'Archivo para Banco',
        ];

        return view('backend.payroll.bank_file.index', compact('accounts', 'banks', 'dates', 'data'));
    }

    /**
     * Generar archivo PDF o Excel.
     */
    public function generate(Request $request)
    {
        $validated = $request->validate([
            'date'                     => 'required|date',
            'company_bank_account_id'  => 'required|integer|exists:company_bank_accounts,id',
            'bank_name'                => 'required|string|max:255',
            'format'                   => 'nullable|string|in:pdf,excel',
        ]);

        $companyId = auth()->user()->company_id ?? 1;
        $date      = $validated['date'];
        $bankName  = $validated['bank_name'];
        $format    = $validated['format'] ?? 'pdf';

        try {
            $companyBankAccount = CompanyBankAccount::forCurrentCompany()
                ->findOrFail($validated['company_bank_account_id']);

            $company = DB::table('companies')->where('id', $companyId)->first();

            // Cuenta del empleado vive en users.bank_name / users.bank_account.
            // Filtro principal (modelo Gilton): users.paying_company_bank_account_id =
            // la cuenta empresarial seleccionada. Si el empleado NO tiene asignada cuenta
            // pagadora todavía (legacy), fallback a filtrar solo por banco destino.
            $data = DB::table('salary_generates as sg')
                ->join('users as u', 'u.id', '=', 'sg.user_id')
                ->where('sg.date', $date)
                ->where('sg.company_id', $companyId)
                ->where('sg.is_calculated', 1)
                ->where('u.bank_name', $bankName)
                ->whereNotNull('u.bank_account')
                ->where('u.bank_account', '!=', '')
                ->whereNull('u.deleted_at')
                ->where(function ($q) use ($companyBankAccount) {
                    // Empleados explícitamente asignados a esta cuenta pagadora
                    $q->where('u.paying_company_bank_account_id', $companyBankAccount->id)
                      // O empleados sin asignación (legacy) si nadie tiene asignada — fallback
                      ->orWhereNull('u.paying_company_bank_account_id');
                })
                ->select(
                    'u.id as user_id',
                    'u.name as employee_name',
                    'u.bank_account as account_number',
                    'u.bank_name as bank_name',
                    'u.paying_company_bank_account_id as payer_id',
                    DB::raw('COALESCE(sg.net_salary, sg.amount) as salary')
                )
                ->orderBy('u.name')
                ->get();

            // Si hay AL MENOS UN empleado con asignación explícita a esta cuenta,
            // dejamos sólo los explícitos (descartamos los "sin asignar"). Si no hay
            // ninguno asignado todavía, devolvemos los sin asignar (fallback inicial).
            $explicitos = $data->filter(fn($r) => $r->payer_id == $companyBankAccount->id);
            if ($explicitos->count() > 0) {
                $data = $explicitos->values();
            }

            if ($data->isEmpty()) {
                Toastr::warning('No hay empleados con planilla calculada para esa fecha y banco.', 'Sin datos');
                return redirect()->route('hrm.payroll.bank_file.index');
            }

            if ($format === 'excel') {
                $filename = 'archivo_banco_' . str_replace(' ', '_', strtolower($bankName)) . '_' . $date . '.xlsx';
                return Excel::download(
                    new BankFileExport($data, $companyBankAccount, $date, $company, $bankName),
                    $filename
                );
            }

            // PDF (default)
            $filename = 'archivo_banco_' . str_replace(' ', '_', strtolower($bankName)) . '_' . $date . '.pdf';

            $pdf = Pdf::loadView('backend.payroll.bank_file.pdf', [
                'data'               => $data,
                'companyBankAccount' => $companyBankAccount,
                'date'               => $date,
                'company'            => $company,
                'bankName'           => $bankName,
                'range'              => $this->computeFortnightRange($date),
            ])->setPaper('letter', 'portrait');

            return $pdf->download($filename);
        } catch (\Throwable $th) {
            Toastr::error('Error al generar el archivo: ' . $th->getMessage(), 'Error');
            return redirect()->route('hrm.payroll.bank_file.index');
        }
    }

    /**
     * Convertir una fecha de planilla en su rango quincenal.
     * - día <= 15 → 1 al 15 del mes
     * - día > 15  → 16 al último día del mes
     *
     * Devuelve ['start' => '1', 'end' => '15', 'month' => 'Marzo', 'year' => 2026, 'label' => '01 al 15 de Marzo de 2026']
     */
    private function computeFortnightRange(string $date): array
    {
        $mesesEs = [
            1 => 'Enero', 2 => 'Febrero', 3 => 'Marzo', 4 => 'Abril',
            5 => 'Mayo', 6 => 'Junio', 7 => 'Julio', 8 => 'Agosto',
            9 => 'Septiembre', 10 => 'Octubre', 11 => 'Noviembre', 12 => 'Diciembre',
        ];

        $d = Carbon::parse($date);
        $day = (int) $d->day;
        $month = $mesesEs[(int) $d->month] ?? $d->format('F');
        $year = $d->year;

        if ($day <= 15) {
            $start = 1;
            $end = 15;
        } else {
            $start = 16;
            $end = (int) $d->copy()->endOfMonth()->day;
        }

        $label = str_pad((string) $start, 2, '0', STR_PAD_LEFT)
            . ' al '
            . str_pad((string) $end, 2, '0', STR_PAD_LEFT)
            . ' de ' . $month . ' de ' . $year;

        return compact('start', 'end', 'month', 'year', 'label');
    }
}
