<?php

namespace App\Http\Controllers\Modules\Employee;

use App\Http\Controllers\Controller;
use App\Http\Traits\HasRoleViews;
use App\Models\EmployeeDetails;
use App\Models\EmployeeSalaryDetail;
use Illuminate\Http\Request;
use Illuminate\Http\JsonResponse;
use Illuminate\Support\Facades\Log;
use Barryvdh\DomPDF\Facade\Pdf;
use ZipArchive;

/**
 * Unified Salary Detail Controller
 *
 * Handles employee monthly salary details (EmployeeSalaryDetail model).
 * This is different from SalaryStructureController which handles fixed salary structures.
 */
class SalaryDetailController extends Controller
{
    use HasRoleViews;

    /**
     * Display the salary details (payroll) page
     */
    public function index()
    {
        $employees = EmployeeDetails::where('status', EmployeeDetails::STATUS_ACTIVE)->get();
        $salaryDetails = EmployeeSalaryDetail::with('employee')->orderByDesc('id')->get();

        // Pre-process employees for JavaScript (avoids Blade @json parser issues with closures)
        $employeesForJs = $employees->map(function ($emp) {
            return [
                'id' => $emp->id,
                'employee_id' => $emp->employee_id,
                'name' => $emp->name,
                'department' => $emp->department,
            ];
        })->values();

        return $this->roleView('employee.employeepayroll', compact('employees', 'salaryDetails', 'employeesForJs'));
    }

    /**
     * Get salary data for DataTable/filter
     */
    public function data(Request $request): JsonResponse
    {
        $query = EmployeeSalaryDetail::with('employee');

        if ($request->has('month_year') && $request->month_year) {
            $query->where('month_year', $request->month_year);
        }

        if ($request->has('department') && $request->department) {
            $query->whereHas('employee', function ($q) use ($request) {
                $q->where('department', $request->department);
            });
        }

        if ($request->has('employee_id') && is_array($request->employee_id) && count($request->employee_id) > 0) {
            $query->whereIn('employee_id', $request->employee_id);
        }

        $data = $query->orderByDesc('id')->get();

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

    /**
     * Store a new salary detail
     */
    public function store(Request $request)
    {
        $validated = $request->validate([
            'employee_id' => 'required|exists:employees,id',
            'month_year' => 'required|string',
            'basic_da' => 'nullable|numeric|min:0',
            'hra' => 'nullable|numeric|min:0',
            'conveyance' => 'nullable|numeric|min:0',
            'washing_allowance' => 'nullable|numeric|min:0',
            'fixed_salary' => 'nullable|numeric|min:0',
            'no_of_days_worked' => 'nullable|integer|min:0',
            'leave_given' => 'nullable|integer|min:0',
            'total_days_for_salary' => 'nullable|integer|min:0',
            'drawn_salary' => 'nullable|numeric|min:0',
            'incentive_hrs' => 'nullable|numeric|min:0',
            'incentive_rate' => 'nullable|numeric|min:0',
            'incentive_amount' => 'nullable|numeric|min:0',
            'attendance_bonus' => 'nullable|numeric|min:0',
            'gross_salary' => 'nullable|numeric|min:0',
            'esi' => 'nullable|numeric|min:0',
            'pf' => 'nullable|numeric|min:0',
            'pt' => 'nullable|numeric|min:0',
            'advance_deduction' => 'nullable|numeric|min:0',
            'net_salary' => 'nullable|numeric|min:0',
            'payment_mode' => 'nullable|string',
        ]);

        try {
            $employee = EmployeeDetails::findOrFail($validated['employee_id']);
            $validated['employee_name'] = $employee->name;

            $salary = EmployeeSalaryDetail::create($validated);

            if ($request->ajax()) {
                return response()->json([
                    'success' => true,
                    'message' => 'Salary details added successfully',
                    'data' => $salary,
                ]);
            }

            return redirect()->back()->with('success', 'Salary details added successfully.');
        } catch (\Exception $e) {
            Log::error('Failed to store salary detail: ' . $e->getMessage());

            if ($request->ajax()) {
                return response()->json([
                    'success' => false,
                    'message' => 'Failed to save salary details',
                ], 500);
            }

            return redirect()->back()->with('error', 'Failed to save salary details.');
        }
    }

    /**
     * Get salary detail for editing
     */
    public function show(int $id): JsonResponse
    {
        try {
            $salary = EmployeeSalaryDetail::with('employee')->findOrFail($id);

            return response()->json($salary);
        } catch (\Exception $e) {
            return response()->json([
                'success' => false,
                'message' => 'Salary record not found',
            ], 404);
        }
    }

    /**
     * Update salary detail
     */
    public function update(Request $request, int $id)
    {
        $validated = $request->validate([
            'month_year' => 'nullable|string',
            'basic_da' => 'nullable|numeric|min:0',
            'hra' => 'nullable|numeric|min:0',
            'conveyance' => 'nullable|numeric|min:0',
            'washing_allowance' => 'nullable|numeric|min:0',
            'fixed_salary' => 'nullable|numeric|min:0',
            'no_of_days_worked' => 'nullable|integer|min:0',
            'leave_given' => 'nullable|integer|min:0',
            'total_days_for_salary' => 'nullable|integer|min:0',
            'drawn_salary' => 'nullable|numeric|min:0',
            'incentive_hrs' => 'nullable|numeric|min:0',
            'incentive_rate' => 'nullable|numeric|min:0',
            'incentive_amount' => 'nullable|numeric|min:0',
            'attendance_bonus' => 'nullable|numeric|min:0',
            'gross_salary' => 'nullable|numeric|min:0',
            'esi' => 'nullable|numeric|min:0',
            'pf' => 'nullable|numeric|min:0',
            'pt' => 'nullable|numeric|min:0',
            'advance_deduction' => 'nullable|numeric|min:0',
            'net_salary' => 'nullable|numeric|min:0',
            'payment_mode' => 'nullable|string',
        ]);

        try {
            $salary = EmployeeSalaryDetail::findOrFail($id);
            $salary->update($validated);

            if ($request->ajax()) {
                return response()->json([
                    'success' => true,
                    'message' => 'Salary details updated successfully',
                    'data' => $salary->fresh(),
                ]);
            }

            return redirect()->back()->with('success', 'Salary details updated successfully.');
        } catch (\Exception $e) {
            Log::error('Failed to update salary detail: ' . $e->getMessage());

            if ($request->ajax()) {
                return response()->json([
                    'success' => false,
                    'message' => 'Failed to update salary details',
                ], 500);
            }

            return redirect()->back()->with('error', 'Failed to update salary details.');
        }
    }

    /**
     * Delete salary detail
     */
    public function destroy(int $id): JsonResponse
    {
        if (!$this->isSuperAdmin() && !$this->isManager()) {
            return response()->json(['success' => false, 'message' => 'Unauthorized.'], 403);
        }

        try {
            $salary = EmployeeSalaryDetail::findOrFail($id);
            $salary->delete();

            return response()->json([
                'success' => true,
                'message' => 'Salary record deleted successfully',
            ]);
        } catch (\Exception $e) {
            Log::error('Failed to delete salary detail: ' . $e->getMessage());
            return response()->json([
                'success' => false,
                'message' => 'Failed to delete salary record',
            ], 500);
        }
    }

    /**
     * Generate payslip PDF
     */
    public function payslip(int $id)
    {
        $salary = EmployeeSalaryDetail::with('employee')->findOrFail($id);

        $pdf = Pdf::loadView('pdf.payslip', compact('salary'));
        $pdf->setPaper('a4', 'portrait');

        $fileName = "payslip_{$salary->employee->employee_id}_{$salary->month_year}.pdf";

        return $pdf->download($fileName);
    }

    /**
     * Approve salary detail
     */
    public function approve(int $id): JsonResponse
    {
        if (!$this->isSuperAdmin()) {
            return response()->json([
                'success' => false,
                'message' => 'Unauthorized',
            ], 403);
        }

        try {
            $salary = EmployeeSalaryDetail::findOrFail($id);
            $salary->update([
                'approval_status' => EmployeeSalaryDetail::STATUS_APPROVED,
                'approved_at' => now(),
            ]);

            return response()->json([
                'success' => true,
                'message' => 'Salary record approved successfully',
            ]);
        } catch (\Exception $e) {
            Log::error('Failed to approve salary: ' . $e->getMessage());
            return response()->json([
                'success' => false,
                'message' => 'Failed to approve salary record',
            ], 500);
        }
    }

    /**
     * Reject salary detail
     */
    public function reject(Request $request, int $id): JsonResponse
    {
        if (!$this->isSuperAdmin()) {
            return response()->json([
                'success' => false,
                'message' => 'Unauthorized',
            ], 403);
        }

        $validated = $request->validate([
            'reason' => 'required|string|max:500',
        ]);

        try {
            $salary = EmployeeSalaryDetail::findOrFail($id);
            $salary->update([
                'approval_status' => EmployeeSalaryDetail::STATUS_REJECTED,
                'rejection_reason' => $validated['reason'],
            ]);

            return response()->json([
                'success' => true,
                'message' => 'Salary record rejected',
            ]);
        } catch (\Exception $e) {
            Log::error('Failed to reject salary: ' . $e->getMessage());
            return response()->json([
                'success' => false,
                'message' => 'Failed to reject salary record',
            ], 500);
        }
    }

    /**
     * Bulk approve salary details
     */
    public function bulkApprove(Request $request): JsonResponse
    {
        if (!$this->isSuperAdmin()) {
            return response()->json([
                'success' => false,
                'message' => 'Unauthorized',
            ], 403);
        }

        $validated = $request->validate([
            'ids' => 'required|array',
            'ids.*' => 'integer|exists:employee_salary_details,id',
        ]);

        try {
            $count = EmployeeSalaryDetail::whereIn('id', $validated['ids'])
                ->update([
                    'approval_status' => EmployeeSalaryDetail::STATUS_APPROVED,
                    'approved_at' => now(),
                ]);

            return response()->json([
                'success' => true,
                'message' => "Approved $count salary record(s) successfully",
            ]);
        } catch (\Exception $e) {
            Log::error('Failed to bulk approve salaries: ' . $e->getMessage());
            return response()->json([
                'success' => false,
                'message' => 'Failed to approve salary records',
            ], 500);
        }
    }

    /**
     * Bulk reject salary details
     */
    public function bulkReject(Request $request): JsonResponse
    {
        if (!$this->isSuperAdmin()) {
            return response()->json([
                'success' => false,
                'message' => 'Unauthorized',
            ], 403);
        }

        $validated = $request->validate([
            'ids' => 'required|array',
            'ids.*' => 'integer|exists:employee_salary_details,id',
            'reason' => 'required|string|max:500',
        ]);

        try {
            $count = EmployeeSalaryDetail::whereIn('id', $validated['ids'])
                ->update([
                    'approval_status' => EmployeeSalaryDetail::STATUS_REJECTED,
                    'rejection_reason' => $validated['reason'],
                ]);

            return response()->json([
                'success' => true,
                'message' => "Rejected $count salary record(s)",
            ]);
        } catch (\Exception $e) {
            Log::error('Failed to bulk reject salaries: ' . $e->getMessage());
            return response()->json([
                'success' => false,
                'message' => 'Failed to reject salary records',
            ], 500);
        }
    }

    /**
     * Bulk download payslips as ZIP
     */
    public function bulkDownload(Request $request)
    {
        $validated = $request->validate([
            'ids' => 'required|array',
            'ids.*' => 'integer|exists:employee_salary_details,id',
        ]);

        $salaries = EmployeeSalaryDetail::with('employee')
            ->whereIn('id', $validated['ids'])
            ->get();

        if ($salaries->isEmpty()) {
            return back()->with('error', 'No salary records found.');
        }

        $zipFileName = 'payslips_' . now()->format('Ymd_His') . '.zip';
        $zipPath = storage_path('app/temp/' . $zipFileName);

        // Ensure temp directory exists
        if (!file_exists(storage_path('app/temp'))) {
            mkdir(storage_path('app/temp'), 0755, true);
        }

        $zip = new ZipArchive();

        if ($zip->open($zipPath, ZipArchive::CREATE | ZipArchive::OVERWRITE) !== true) {
            return back()->with('error', 'Failed to create ZIP file.');
        }

        foreach ($salaries as $salary) {
            $pdf = Pdf::loadView('pdf.payslip', compact('salary'));
            $pdfContent = $pdf->output();

            $fileName = "payslip_{$salary->employee->employee_id}_{$salary->month_year}.pdf";
            $zip->addFromString($fileName, $pdfContent);
        }

        $zip->close();

        return response()->download($zipPath, $zipFileName)->deleteFileAfterSend(true);
    }
}
