<?php

namespace App\Controllers\Salary;

use App\Controllers\Controller;
use App\Models\SalaryEmployee;
use App\Models\SalaryPaymentSchedule;
use App\Models\SalaryApprovalLog;
use App\Models\SalaryPayment;
use App\Traits\ApprovalPeriodTrait;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Illuminate\Validation\ValidationException;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Illuminate\Database\QueryException;
use Throwable;

class SalaryScheduleController extends Controller
{
    use ApprovalPeriodTrait;
    
    /**
     * Display a listing of salary schedules.
     */
    public function index(Request $request)
    {
        try {
            $query = SalaryPaymentSchedule::with(['employee', 'approver']);
            
            // Apply filters
            if ($request->filled('search')) {
                $query->whereHas('employee', function($q) use ($request) {
                    $q->where('name', 'like', "%{$request->search}%");
                });
            }
            
            if ($request->filled('status')) {
                $query->where('status', $request->status);
            }
            
            if ($request->filled('type')) {
                $query->where('type', $request->type);
            }
            
            if ($request->filled('employee_id')) {
                $query->where('employee_id', $request->employee_id);
            }
            
            if ($request->filled('from_date')) {
                $query->whereDate('scheduled_date', '>=', $request->from_date);
            }
            
            if ($request->filled('to_date')) {
                $query->whereDate('scheduled_date', '<=', $request->to_date);
            }
            
            $schedules = $query->orderBy('scheduled_date', 'asc')->paginate(15);
            
            // Statistics with error handling for each calculation
            $totalSchedules = SalaryPaymentSchedule::count();
            $pendingApprovals = SalaryPaymentSchedule::where('status', 'pending')->count();
            $totalScheduledAmount = SalaryPaymentSchedule::whereIn('status', ['approved', 'pending'])->sum('amount') ?? 0;
            $upcomingCount = SalaryPaymentSchedule::where('status', 'approved')
                ->whereBetween('scheduled_date', [now(), now()->addDays(7)])
                ->count();
            
            $employees = SalaryEmployee::where('status', 'active')->get();
            $approvalInfo = $this->getApprovalInfo();
            
            return view('salary.schedules.index', compact(
                'schedules', 'totalSchedules', 'pendingApprovals', 
                'totalScheduledAmount', 'upcomingCount', 'employees', 'approvalInfo'
            ));
            
        } catch (QueryException $e) {
            Log::error('Database error in schedules index: ' . $e->getMessage(), [
                'sql' => $e->getSql(),
                'bindings' => $e->getBindings(),
                'trace' => $e->getTraceAsString()
            ]);
            
            return back()->with('error', 'Unable to load schedules due to a database error. Please try again.');
            
        } catch (Throwable $e) {
            Log::error('Unexpected error in schedules index: ' . $e->getMessage(), [
                'trace' => $e->getTraceAsString()
            ]);
            
            return back()->with('error', 'An unexpected error occurred. Please try again.');
        }
    }

    /**
     * Show the form for creating a new payment schedule.
     */
    public function create()
    {
        try {
            $employees = SalaryEmployee::where('status', 'active')->get();
            
            if ($employees->isEmpty()) {
                return redirect()->route('salary.schedules.index')
                    ->with('warning', 'No active employees found. Please add employees before creating schedules.');
            }
            
            $approvalInfo = $this->getApprovalInfo();
            
            return view('salary.schedules.create', compact('employees', 'approvalInfo'));
            
        } catch (QueryException $e) {
            Log::error('Database error in schedule create form: ' . $e->getMessage());
            return redirect()->route('salary.schedules.index')
                ->with('error', 'Unable to load the form. Please try again.');
        } catch (Throwable $e) {
            Log::error('Unexpected error in schedule create form: ' . $e->getMessage());
            return redirect()->route('salary.schedules.index')
                ->with('error', 'An unexpected error occurred.');
        }
    }

    /**
     * Store a newly created payment schedule.
     */
    public function store(Request $request)
    {
        try {
            $validated = $request->validate([
                'employee_id' => 'required|exists:salary_employees,id',
                'scheduled_date' => 'required|date|after_or_equal:today',
                'amount' => 'required|numeric|min:0.01|max:99999999.99',
                'type' => 'required|in:regular,advance,adjustment,bonus',
                'notes' => 'nullable|string|max:1000',
            ]);
            
            DB::beginTransaction();
            
            // Check if employee exists and is active
            $employee = SalaryEmployee::find($validated['employee_id']);
            if (!$employee || $employee->status !== 'active') {
                throw new \RuntimeException('Employee not found or is not active.', 404);
            }
            
            // Validate employee has required salary information
            if (!$employee->basic_salary && $validated['type'] === 'regular') {
                throw new \RuntimeException('Employee does not have a basic salary configured.', 422);
            }
            
            // Check for duplicate schedule in the same month
            $existingSchedule = SalaryPaymentSchedule::where('employee_id', $validated['employee_id'])
                ->whereYear('scheduled_date', date('Y', strtotime($validated['scheduled_date'])))
                ->whereMonth('scheduled_date', date('m', strtotime($validated['scheduled_date'])))
                ->whereIn('status', ['pending', 'approved'])
                ->lockForUpdate()
                ->first();
            
            if ($existingSchedule) {
                throw new \RuntimeException(
                    sprintf('Employee already has a %s schedule for %s. Only one schedule per month is allowed.',
                        $existingSchedule->status,
                        date('F Y', strtotime($validated['scheduled_date']))
                    ),
                    409
                );
            }
            
            // Validate amount against employee's salary for regular payments
            if ($validated['type'] === 'regular' && $validated['amount'] > $employee->basic_salary * 1.5) {
                throw new \RuntimeException(
                    sprintf('Regular payment amount (KES %s) exceeds 150%% of basic salary (KES %s).',
                        number_format($validated['amount'], 2),
                        number_format($employee->basic_salary, 2)
                    ),
                    422
                );
            }
            
            // Create schedule
            $schedule = new SalaryPaymentSchedule();
            $schedule->employee_id = $validated['employee_id'];
            $schedule->scheduled_date = $validated['scheduled_date'];
            $schedule->amount = $validated['amount'];
            $schedule->type = $validated['type'];
            $schedule->status = 'pending';
            $schedule->notes = $validated['notes'] ?? null;
            $schedule->deduction_id = null;
            $schedule->installment_number = null;
            $schedule->total_installments = null;
            $schedule->remaining_balance = 0;
            $schedule->payment_id = null;
            
            if (!$schedule->save()) {
                throw new \RuntimeException('Failed to save schedule record.', 500);
            }
            
            // Create approval log
            $logData = [
                'approvable_type' => SalaryPaymentSchedule::class,
                'approvable_id' => $schedule->id,
                'user_id' => Auth::id(),
                'action' => 'requested',
                'comments' => sprintf('Schedule payment request for KES %s (%s)',
                    number_format($validated['amount'], 2),
                    $validated['type']
                ),
            ];
            
            if (!SalaryApprovalLog::create($logData)) {
                Log::warning('Failed to create approval log for schedule ID: ' . $schedule->id);
                // Don't throw exception as this is not critical
            }
            
            DB::commit();
            
            $message = sprintf('Payment schedule created successfully for %s and is pending approval.',
                $employee->name
            );
            
            if ($request->expectsJson() || $request->ajax()) {
                return response()->json([
                    'success' => true,
                    'message' => $message,
                    'data' => [
                        'schedule' => $schedule,
                        'employee' => $employee->only(['id', 'name'])
                    ]
                ], 201);
            }
            
            return redirect()->route('salary.schedules.index')
                ->with('success', $message);
                
        } catch (ValidationException $e) {
            DB::rollBack();
            
            $errors = $e->errors();
            $errorMessages = [];
            foreach ($errors as $field => $messages) {
                $errorMessages[] = implode(', ', $messages);
            }
            
            if ($request->expectsJson() || $request->ajax()) {
                return response()->json([
                    'success' => false,
                    'message' => 'Validation failed: ' . implode('; ', $errorMessages),
                    'errors' => $errors
                ], 422);
            }
            
            return back()
                ->withErrors($e->validator)
                ->withInput()
                ->with('error', 'Please correct the validation errors below.');
            
        } catch (\RuntimeException $e) {
            DB::rollBack();
            
            $statusCode = $e->getCode() >= 400 && $e->getCode() < 600 ? $e->getCode() : 400;
            
            Log::warning('Schedule creation business rule violation: ' . $e->getMessage(), [
                'employee_id' => $request->employee_id,
                'scheduled_date' => $request->scheduled_date,
                'amount' => $request->amount,
                'type' => $request->type,
                'user_id' => Auth::id()
            ]);
            
            if ($request->expectsJson() || $request->ajax()) {
                return response()->json([
                    'success' => false,
                    'message' => $e->getMessage()
                ], $statusCode);
            }
            
            return back()
                ->with('error', $e->getMessage())
                ->withInput();
            
        } catch (QueryException $e) {
            DB::rollBack();
            
            Log::error('Database error creating schedule: ' . $e->getMessage(), [
                'sql' => $e->getSql(),
                'bindings' => $e->getBindings(),
                'request_data' => $request->except('_token'),
                'trace' => $e->getTraceAsString()
            ]);
            
            $errorMessage = 'A database error occurred while creating the schedule. Please try again.';
            
            if ($request->expectsJson() || $request->ajax()) {
                return response()->json([
                    'success' => false,
                    'message' => $errorMessage
                ], 500);
            }
            
            return back()
                ->with('error', $errorMessage)
                ->withInput();
            
        } catch (Throwable $e) {
            DB::rollBack();
            
            Log::error('Unexpected error creating schedule: ' . $e->getMessage(), [
                'exception_class' => get_class($e),
                'request_data' => $request->except('_token'),
                'trace' => $e->getTraceAsString()
            ]);
            
            $errorMessage = 'An unexpected error occurred while creating the schedule. Please try again.';
            
            if (config('app.debug')) {
                $errorMessage .= ' Error: ' . $e->getMessage();
            }
            
            if ($request->expectsJson() || $request->ajax()) {
                return response()->json([
                    'success' => false,
                    'message' => $errorMessage
                ], 500);
            }
            
            return back()
                ->with('error', $errorMessage)
                ->withInput();
        }
    }
    
    /**
     * Approve a schedule with role-based restrictions
     */
    public function approve(Request $request, $id)
    {
        try {
            // Validate request for comments
            if ($request->has('comments') && strlen($request->comments) > 1000) {
                return response()->json([
                    'success' => false,
                    'message' => 'Comments cannot exceed 1000 characters.'
                ], 422);
            }
            
            // Check approval permissions
            $approvalCheck = $this->canApprove();
            if (!$approvalCheck['can_approve']) {
                Log::warning('Unauthorized approval attempt', [
                    'user_id' => Auth::id(),
                    'schedule_id' => $id,
                    'reason' => $approvalCheck['message']
                ]);
                
                return response()->json([
                    'success' => false,
                    'message' => $approvalCheck['message']
                ], 403);
            }
            
            $schedule = SalaryPaymentSchedule::findOrFail($id);
            
            // Check if already approved
            if ($schedule->status === 'approved') {
                return response()->json([
                    'success' => false,
                    'message' => 'This schedule has already been approved.',
                    'approved_by' => $schedule->approver?->name,
                    'approved_at' => $schedule->approved_at?->format('Y-m-d H:i:s')
                ], 400);
            }
            
            if ($schedule->status !== 'pending') {
                return response()->json([
                    'success' => false, 
                    'message' => sprintf('This schedule cannot be approved. Current status: %s', $schedule->status),
                    'allowed_statuses' => ['pending']
                ], 400);
            }
            
            // Additional validation for regular payments
            if ($schedule->type === 'regular' && $schedule->employee) {
                $maxAmount = $schedule->employee->basic_salary * 1.5;
                if ($schedule->amount > $maxAmount) {
                    return response()->json([
                        'success' => false,
                        'message' => sprintf('Cannot approve: Amount (KES %s) exceeds 150%% of basic salary (KES %s)',
                            number_format($schedule->amount, 2),
                            number_format($maxAmount, 2)
                        )
                    ], 422);
                }
            }
            
            DB::beginTransaction();
            
            // Determine approval level
            $isDirector = $this->isDirector();
            $isAdmin = $this->isAdmin();
            $approvalLevel = $isDirector ? 'director' : ($isAdmin ? 'admin' : 'unknown');
            
            $updateData = [
                'status' => 'approved',
                'approved_by' => Auth::id(),
                'approved_at' => now(),
            ];
            
            if (!$schedule->update($updateData)) {
                throw new \RuntimeException('Failed to update schedule status.', 500);
            }
            
            $comments = $request->input('comments', sprintf("Schedule approved by %s", ucfirst($approvalLevel)));
            
            $logData = [
                'approvable_type' => SalaryPaymentSchedule::class,
                'approvable_id' => $schedule->id,
                'user_id' => Auth::id(),
                'action' => 'approved',
                'comments' => $comments,
            ];
            
            if (!SalaryApprovalLog::create($logData)) {
                Log::warning('Failed to create approval log for schedule ID: ' . $schedule->id);
            }
            
            DB::commit();
            
            Log::info('Schedule approved', [
                'schedule_id' => $schedule->id,
                'employee_id' => $schedule->employee_id,
                'amount' => $schedule->amount,
                'approved_by' => Auth::id(),
                'approval_level' => $approvalLevel
            ]);
            
            return response()->json([
                'success' => true,
                'message' => 'Schedule approved successfully',
                'data' => [
                    'approval_level' => $approvalLevel,
                    'approved_at' => now()->toDateTimeString(),
                    'approved_by' => Auth::user()?->name
                ]
            ]);
            
        } catch (ModelNotFoundException $e) {
            DB::rollBack();
            
            Log::warning('Schedule not found for approval', ['schedule_id' => $id]);
            
            return response()->json([
                'success' => false,
                'message' => 'Schedule not found. It may have been deleted.'
            ], 404);
            
        } catch (\RuntimeException $e) {
            DB::rollBack();
            
            Log::error('Runtime error during schedule approval: ' . $e->getMessage(), [
                'schedule_id' => $id,
                'user_id' => Auth::id()
            ]);
            
            return response()->json([
                'success' => false,
                'message' => $e->getMessage()
            ], $e->getCode() ?: 500);
            
        } catch (QueryException $e) {
            DB::rollBack();
            
            Log::error('Database error during schedule approval: ' . $e->getMessage(), [
                'schedule_id' => $id,
                'sql' => $e->getSql()
            ]);
            
            return response()->json([
                'success' => false,
                'message' => 'A database error occurred while approving the schedule.'
            ], 500);
            
        } catch (Throwable $e) {
            DB::rollBack();
            
            Log::error('Unexpected error during schedule approval: ' . $e->getMessage(), [
                'schedule_id' => $id,
                'trace' => $e->getTraceAsString()
            ]);
            
            return response()->json([
                'success' => false,
                'message' => 'An unexpected error occurred. Please try again.'
            ], 500);
        }
    }
    
    /**
     * Reject a schedule
     */
    public function reject(Request $request, $id)
    {
        try {
            $validated = $request->validate([
                'reason' => 'required|string|min:3|max:500'
            ]);
            
            $schedule = SalaryPaymentSchedule::findOrFail($id);
            
            // Check if schedule can be rejected
            if (!in_array($schedule->status, ['pending', 'approved'])) {
                $allowedStatuses = ['pending', 'approved'];
                return response()->json([
                    'success' => false,
                    'message' => sprintf('This schedule cannot be rejected. Current status: %s. Allowed statuses: %s',
                        $schedule->status,
                        implode(', ', $allowedStatuses)
                    )
                ], 400);
            }
            
            DB::beginTransaction();
            
            $reason = $validated['reason'];
            
            $updateData = [
                'status' => 'cancelled',
                'rejected_by' => Auth::id(),
                'rejected_at' => now(),
                'rejection_reason' => $reason
            ];
            
            if (!$schedule->update($updateData)) {
                throw new \RuntimeException('Failed to update schedule status.', 500);
            }
            
            $logData = [
                'approvable_type' => SalaryPaymentSchedule::class,
                'approvable_id' => $schedule->id,
                'user_id' => Auth::id(),
                'action' => 'rejected',
                'comments' => sprintf("Schedule rejected. Reason: %s", $reason),
            ];
            
            if (!SalaryApprovalLog::create($logData)) {
                Log::warning('Failed to create rejection log for schedule ID: ' . $schedule->id);
            }
            
            DB::commit();
            
            Log::info('Schedule rejected', [
                'schedule_id' => $schedule->id,
                'employee_id' => $schedule->employee_id,
                'reason' => $reason,
                'rejected_by' => Auth::id()
            ]);
            
            return response()->json([
                'success' => true,
                'message' => 'Schedule rejected successfully',
                'data' => [
                    'rejected_at' => now()->toDateTimeString(),
                    'rejected_by' => Auth::user()?->name
                ]
            ]);
            
        } catch (ValidationException $e) {
            return response()->json([
                'success' => false,
                'message' => 'Validation failed: A rejection reason is required (min 3, max 500 characters)',
                'errors' => $e->errors()
            ], 422);
            
        } catch (ModelNotFoundException $e) {
            DB::rollBack();
            
            return response()->json([
                'success' => false,
                'message' => 'Schedule not found.'
            ], 404);
            
        } catch (\RuntimeException $e) {
            DB::rollBack();
            
            Log::error('Runtime error during schedule rejection: ' . $e->getMessage(), [
                'schedule_id' => $id
            ]);
            
            return response()->json([
                'success' => false,
                'message' => $e->getMessage()
            ], $e->getCode() ?: 500);
            
        } catch (Throwable $e) {
            DB::rollBack();
            
            Log::error('Unexpected error during schedule rejection: ' . $e->getMessage(), [
                'schedule_id' => $id,
                'trace' => $e->getTraceAsString()
            ]);
            
            return response()->json([
                'success' => false,
                'message' => 'An unexpected error occurred while rejecting the schedule.'
            ], 500);
        }
    }
    
    /**
     * Display the specified schedule.
     */
    public function show($id)
    {
        try {
            $schedule = SalaryPaymentSchedule::with([
                'employee',
                'approver',
                'rejector',
                'deduction'
            ])->findOrFail($id);
            
            // Add additional calculated data
            $scheduleData = $schedule->toArray();
            $scheduleData['formatted_amount'] = number_format($schedule->amount, 2);
            $scheduleData['formatted_scheduled_date'] = $schedule->scheduled_date->format('F j, Y');
            $scheduleData['is_installment'] = $schedule->isInstallment();
            $scheduleData['can_edit'] = $schedule->status === 'pending';
            $scheduleData['can_delete'] = !in_array($schedule->status, ['paid', 'deducted']);
            $scheduleData['can_approve'] = $schedule->status === 'pending' && $this->canApprove()['can_approve'];
            $scheduleData['can_reject'] = in_array($schedule->status, ['pending', 'approved']);
            
            if ($schedule->approver) {
                $scheduleData['approver_name'] = $schedule->approver->name;
            }
            
            if ($schedule->rejector) {
                $scheduleData['rejector_name'] = $schedule->rejector->name;
            }
            
            return response()->json([
                'success' => true,
                'data' => $scheduleData
            ]);
            
        } catch (ModelNotFoundException $e) {
            Log::warning('Schedule not found for view', ['schedule_id' => $id]);
            
            return response()->json([
                'success' => false,
                'message' => 'Schedule not found. It may have been deleted.'
            ], 404);
            
        } catch (Throwable $e) {
            Log::error('Error loading schedule details: ' . $e->getMessage(), [
                'schedule_id' => $id,
                'trace' => $e->getTraceAsString()
            ]);
            
            return response()->json([
                'success' => false,
                'message' => 'Unable to load schedule details. Please try again.'
            ], 500);
        }
    }

    /**
     * Show the form for editing the specified schedule.
     */
    public function edit($id)
    {
        try {
            $schedule = SalaryPaymentSchedule::findOrFail($id);
            
            if ($schedule->status !== 'pending') {
                return response()->json([
                    'success' => false,
                    'message' => sprintf('Only pending schedules can be edited. Current status: %s', $schedule->status)
                ], 422);
            }
            
            $employees = SalaryEmployee::where('status', 'active')->get();
            
            return response()->json([
                'success' => true,
                'data' => [
                    'schedule' => $schedule,
                    'employees' => $employees,
                    'types' => ['regular', 'advance', 'adjustment', 'bonus']
                ]
            ]);
            
        } catch (ModelNotFoundException $e) {
            return response()->json([
                'success' => false,
                'message' => 'Schedule not found.'
            ], 404);
            
        } catch (Throwable $e) {
            Log::error('Error loading schedule for edit: ' . $e->getMessage(), [
                'schedule_id' => $id
            ]);
            
            return response()->json([
                'success' => false,
                'message' => 'Unable to load schedule for editing.'
            ], 500);
        }
    }

    /**
     * Update the specified schedule in storage.
     */
    public function update(Request $request, $id)
    {
        try {
            $schedule = SalaryPaymentSchedule::findOrFail($id);
            
            if ($schedule->status !== 'pending') {
                return response()->json([
                    'success' => false,
                    'message' => sprintf('Only pending schedules can be updated. Current status: %s', $schedule->status)
                ], 422);
            }
            
            $validated = $request->validate([
                'employee_id' => 'required|exists:salary_employees,id',
                'scheduled_date' => 'required|date|after_or_equal:today',
                'amount' => 'required|numeric|min:0.01|max:99999999.99',
                'type' => 'required|in:regular,advance,adjustment,bonus',
                'notes' => 'nullable|string|max:1000',
            ]);
            
            DB::beginTransaction();
            
            // Check for duplicate schedule in the same month (excluding current)
            $existingSchedule = SalaryPaymentSchedule::where('employee_id', $validated['employee_id'])
                ->where('id', '!=', $schedule->id)
                ->whereYear('scheduled_date', date('Y', strtotime($validated['scheduled_date'])))
                ->whereMonth('scheduled_date', date('m', strtotime($validated['scheduled_date'])))
                ->whereIn('status', ['pending', 'approved'])
                ->first();
            
            if ($existingSchedule) {
                throw new \RuntimeException(
                    sprintf('Another %s schedule already exists for %s.',
                        $existingSchedule->status,
                        date('F Y', strtotime($validated['scheduled_date']))
                    ),
                    409
                );
            }
            
            $oldData = $schedule->getOriginal();
            
            if (!$schedule->update($validated)) {
                throw new \RuntimeException('Failed to update schedule.', 500);
            }
            
            // Log the update
            SalaryApprovalLog::create([
                'approvable_type' => SalaryPaymentSchedule::class,
                'approvable_id' => $schedule->id,
                'user_id' => Auth::id(),
                'action' => 'updated',
                'comments' => sprintf('Schedule updated. Changes: amount from %s to %s, date from %s to %s',
                    number_format($oldData['amount'], 2),
                    number_format($validated['amount'], 2),
                    $oldData['scheduled_date'],
                    $validated['scheduled_date']
                ),
            ]);
            
            DB::commit();
            
            return response()->json([
                'success' => true,
                'message' => 'Schedule updated successfully',
                'data' => $schedule->fresh()
            ]);
            
        } catch (ValidationException $e) {
            DB::rollBack();
            
            return response()->json([
                'success' => false,
                'message' => 'Validation failed',
                'errors' => $e->errors()
            ], 422);
            
        } catch (ModelNotFoundException $e) {
            DB::rollBack();
            
            return response()->json([
                'success' => false,
                'message' => 'Schedule not found.'
            ], 404);
            
        } catch (\RuntimeException $e) {
            DB::rollBack();
            
            return response()->json([
                'success' => false,
                'message' => $e->getMessage()
            ], $e->getCode() ?: 400);
            
        } catch (Throwable $e) {
            DB::rollBack();
            
            Log::error('Error updating schedule: ' . $e->getMessage(), [
                'schedule_id' => $id,
                'trace' => $e->getTraceAsString()
            ]);
            
            return response()->json([
                'success' => false,
                'message' => 'An unexpected error occurred while updating the schedule.'
            ], 500);
        }
    }

    /**
     * Remove the specified schedule from storage.
     */
    public function destroy($id)
    {
        try {
            $schedule = SalaryPaymentSchedule::findOrFail($id);
            
            // Don't allow deletion of already processed schedules
            if (in_array($schedule->status, ['paid', 'deducted'])) {
                return response()->json([
                    'success' => false,
                    'message' => sprintf('Cannot delete a schedule that has already been %s.', $schedule->status)
                ], 422);
            }
            
            DB::beginTransaction();
            
            // Log before deletion
            SalaryApprovalLog::create([
                'approvable_type' => SalaryPaymentSchedule::class,
                'approvable_id' => $schedule->id,
                'user_id' => Auth::id(),
                'action' => 'deleted',
                'comments' => sprintf('Schedule deleted. Amount: %s, Date: %s, Type: %s',
                    number_format($schedule->amount, 2),
                    $schedule->scheduled_date,
                    $schedule->type
                ),
            ]);
            
            if (!$schedule->delete()) {
                throw new \RuntimeException('Failed to delete schedule.', 500);
            }
            
            DB::commit();
            
            Log::info('Schedule deleted', [
                'schedule_id' => $id,
                'deleted_by' => Auth::id(),
                'original_status' => $schedule->status
            ]);
            
            return response()->json([
                'success' => true,
                'message' => 'Schedule deleted successfully'
            ]);
            
        } catch (ModelNotFoundException $e) {
            DB::rollBack();
            
            return response()->json([
                'success' => false,
                'message' => 'Schedule not found.'
            ], 404);
            
        } catch (\RuntimeException $e) {
            DB::rollBack();
            
            return response()->json([
                'success' => false,
                'message' => $e->getMessage()
            ], $e->getCode() ?: 500);
            
        } catch (Throwable $e) {
            DB::rollBack();
            
            Log::error('Error deleting schedule: ' . $e->getMessage(), [
                'schedule_id' => $id,
                'trace' => $e->getTraceAsString()
            ]);
            
            return response()->json([
                'success' => false,
                'message' => 'An unexpected error occurred while deleting the schedule.'
            ], 500);
        }
    }
    
    /**
     * Bulk approve schedules
     */
    public function bulkApprove(Request $request)
    {
        try {
            // Check approval permissions
            $approvalCheck = $this->canApprove();
            if (!$approvalCheck['can_approve']) {
                return response()->json([
                    'success' => false,
                    'message' => $approvalCheck['message']
                ], 403);
            }
            
            $validated = $request->validate([
                'ids' => 'required|array|min:1|max:100',
                'ids.*' => 'exists:salary_payment_schedules,id'
            ]);
            
            $ids = $validated['ids'];
            
            DB::beginTransaction();
            
            // Get only pending schedules
            $schedules = SalaryPaymentSchedule::whereIn('id', $ids)
                ->where('status', 'pending')
                ->get();
            
            if ($schedules->isEmpty()) {
                DB::rollBack();
                return response()->json([
                    'success' => false,
                    'message' => 'No pending schedules found to approve.'
                ], 404);
            }
            
            $approvedCount = 0;
            $errors = [];
            
            foreach ($schedules as $schedule) {
                try {
                    $schedule->update([
                        'status' => 'approved',
                        'approved_by' => Auth::id(),
                        'approved_at' => now()
                    ]);
                    
                    SalaryApprovalLog::create([
                        'approvable_type' => SalaryPaymentSchedule::class,
                        'approvable_id' => $schedule->id,
                        'user_id' => Auth::id(),
                        'action' => 'approved',
                        'comments' => 'Bulk approval'
                    ]);
                    
                    $approvedCount++;
                    
                } catch (\Exception $e) {
                    $errors[] = "Schedule ID {$schedule->id}: " . $e->getMessage();
                    Log::error('Bulk approval failed for schedule', [
                        'schedule_id' => $schedule->id,
                        'error' => $e->getMessage()
                    ]);
                }
            }
            
            DB::commit();
            
            $message = sprintf("Successfully approved %d of %d schedule(s).", $approvedCount, count($ids));
            
            if (!empty($errors)) {
                $message .= " Errors: " . implode('; ', $errors);
            }
            
            return response()->json([
                'success' => true,
                'message' => $message,
                'data' => [
                    'approved_count' => $approvedCount,
                    'total_selected' => count($ids),
                    'errors' => $errors
                ]
            ]);
            
        } catch (ValidationException $e) {
            return response()->json([
                'success' => false,
                'message' => 'Validation failed: Please select at least one schedule (max 100)',
                'errors' => $e->errors()
            ], 422);
            
        } catch (Throwable $e) {
            DB::rollBack();
            
            Log::error('Error in bulk approval: ' . $e->getMessage(), [
                'trace' => $e->getTraceAsString()
            ]);
            
            return response()->json([
                'success' => false,
                'message' => 'An error occurred during bulk approval. Please try again.'
            ], 500);
        }
    }

    /**
     * Bulk delete schedules
     */
    public function bulkDelete(Request $request)
    {
        try {
            $validated = $request->validate([
                'ids' => 'required|array|min:1|max:100',
                'ids.*' => 'exists:salary_payment_schedules,id'
            ]);
            
            $ids = $validated['ids'];
            
            DB::beginTransaction();
            
            // Get deletable schedules (not processed)
            $schedules = SalaryPaymentSchedule::whereIn('id', $ids)
                ->whereNotIn('status', ['paid', 'deducted'])
                ->get();
            
            if ($schedules->isEmpty()) {
                DB::rollBack();
                return response()->json([
                    'success' => false,
                    'message' => 'No deletable schedules found. Schedules with status "paid" or "deducted" cannot be deleted.'
                ], 404);
            }
            
            $deletedCount = 0;
            $errors = [];
            
            foreach ($schedules as $schedule) {
                try {
                    SalaryApprovalLog::create([
                        'approvable_type' => SalaryPaymentSchedule::class,
                        'approvable_id' => $schedule->id,
                        'user_id' => Auth::id(),
                        'action' => 'deleted',
                        'comments' => 'Bulk deletion'
                    ]);
                    
                    $schedule->delete();
                    $deletedCount++;
                    
                } catch (\Exception $e) {
                    $errors[] = "Schedule ID {$schedule->id}: " . $e->getMessage();
                    Log::error('Bulk deletion failed for schedule', [
                        'schedule_id' => $schedule->id,
                        'error' => $e->getMessage()
                    ]);
                }
            }
            
            DB::commit();
            
            $message = sprintf("Successfully deleted %d of %d schedule(s).", $deletedCount, count($ids));
            
            if (!empty($errors)) {
                $message .= " Errors: " . implode('; ', $errors);
            }
            
            return response()->json([
                'success' => true,
                'message' => $message,
                'data' => [
                    'deleted_count' => $deletedCount,
                    'total_selected' => count($ids),
                    'errors' => $errors
                ]
            ]);
            
        } catch (ValidationException $e) {
            return response()->json([
                'success' => false,
                'message' => 'Validation failed: Please select at least one schedule (max 100)',
                'errors' => $e->errors()
            ], 422);
            
        } catch (Throwable $e) {
            DB::rollBack();
            
            Log::error('Error in bulk deletion: ' . $e->getMessage(), [
                'trace' => $e->getTraceAsString()
            ]);
            
            return response()->json([
                'success' => false,
                'message' => 'An error occurred during bulk deletion. Please try again.'
            ], 500);
        }
    }

    /**
     * Export schedules to CSV
     */
    public function export(Request $request)
    {
        try {
            $query = SalaryPaymentSchedule::with('employee');
            
            // Apply filters
            if ($request->filled('status')) {
                $query->where('status', $request->status);
            }
            
            if ($request->filled('type')) {
                $query->where('type', $request->type);
            }
            
            if ($request->filled('employee_id')) {
                $query->where('employee_id', $request->employee_id);
            }
            
            if ($request->filled('from_date')) {
                $query->whereDate('scheduled_date', '>=', $request->from_date);
            }
            
            if ($request->filled('to_date')) {
                $query->whereDate('scheduled_date', '<=', $request->to_date);
            }
            
            $schedules = $query->orderBy('scheduled_date', 'desc')->get();
            
            if ($schedules->isEmpty()) {
                if ($request->expectsJson() || $request->ajax()) {
                    return response()->json([
                        'success' => false,
                        'message' => 'No schedules found to export with the selected filters.'
                    ], 404);
                }
                
                return back()->with('warning', 'No schedules found to export with the selected filters.');
            }
            
            // Limit export to prevent memory issues
            $maxExport = 10000;
            if ($schedules->count() > $maxExport) {
                Log::warning('Export limited', [
                    'total' => $schedules->count(),
                    'limit' => $maxExport
                ]);
                $schedules = $schedules->take($maxExport);
            }
            
            $filename = sprintf('schedules_export_%s.csv', date('Y-m-d_His'));
            
            $headers = [
                'Content-Type' => 'text/csv; charset=UTF-8',
                'Content-Disposition' => 'attachment; filename="' . $filename . '"',
                'Cache-Control' => 'private, max-age=0, must-revalidate',
                'Pragma' => 'public'
            ];
            
            $callback = function() use ($schedules) {
                $file = fopen('php://output', 'w');
                
                if ($file === false) {
                    throw new \RuntimeException('Unable to create output stream.');
                }
                
                // Add UTF-8 BOM for Excel compatibility
                fputs($file, "\xEF\xBB\xBF");
                
                // Headers
                $headers = [
                    'ID', 'Employee Name', 'Employee Code', 'Amount (KES)', 'Type', 
                    'Scheduled Date', 'Status', 'Is Installment', 'Approved By', 
                    'Approved At', 'Rejected By', 'Rejected At', 'Rejection Reason', 'Notes'
                ];
                fputcsv($file, $headers);
                
                // Data rows
                foreach ($schedules as $schedule) {
                    try {
                        fputcsv($file, [
                            $schedule->id,
                            $schedule->employee?->name ?? 'N/A',
                            $schedule->employee?->employee_code ?? 'N/A',
                            number_format($schedule->amount, 2),
                            ucfirst($schedule->type),
                            $schedule->scheduled_date->format('Y-m-d'),
                            ucfirst($schedule->status),
                            $schedule->isInstallment() ? 'Yes' : 'No',
                            $schedule->approver?->name ?? 'N/A',
                            $schedule->approved_at?->format('Y-m-d H:i:s') ?? 'N/A',
                            $schedule->rejector?->name ?? 'N/A',
                            $schedule->rejected_at?->format('Y-m-d H:i:s') ?? 'N/A',
                            $schedule->rejection_reason ?? 'N/A',
                            $schedule->notes ?? ''
                        ]);
                    } catch (\Exception $e) {
                        Log::error('Error writing schedule to CSV', [
                            'schedule_id' => $schedule->id,
                            'error' => $e->getMessage()
                        ]);
                        // Continue with next record
                    }
                }
                
                fclose($file);
            };
            
            return response()->stream($callback, 200, $headers);
            
        } catch (\RuntimeException $e) {
            Log::error('Export stream error: ' . $e->getMessage());
            
            if ($request->expectsJson() || $request->ajax()) {
                return response()->json([
                    'success' => false,
                    'message' => 'Unable to generate export file.'
                ], 500);
            }
            
            return back()->with('error', 'Unable to generate export file. Please try again.');
            
        } catch (Throwable $e) {
            Log::error('Error exporting schedules: ' . $e->getMessage(), [
                'trace' => $e->getTraceAsString()
            ]);
            
            if ($request->expectsJson() || $request->ajax()) {
                return response()->json([
                    'success' => false,
                    'message' => 'Failed to export schedules: ' . $e->getMessage()
                ], 500);
            }
            
            return back()->with('error', 'Failed to export schedules. Please try again.');
        }
    }

    /**
     * Process schedule immediately
     */
    public function processNow(SalaryPaymentSchedule $schedule)
    {
        try {
            // Validate schedule status
            if ($schedule->status !== 'approved') {
                return back()->with('error', sprintf('Schedule must be approved first. Current status: %s', $schedule->status));
            }
            
            // Check if already processed
            if ($schedule->payment_id) {
                return back()->with('error', 'This schedule has already been processed.');
            }
            
            // Check if employee already has payment for this month
            $existingPayment = SalaryPayment::where('employee_id', $schedule->employee_id)
                ->whereYear('payment_date', now()->year)
                ->whereMonth('payment_date', now()->month)
                ->whereIn('status', ['approved', 'processed'])
                ->first();
            
            if ($existingPayment) {
                return back()->with('error', sprintf('Employee already has a payment (%s) for this month.',
                    $existingPayment->status));
            }
            
            DB::beginTransaction();
            
            // Create payment from schedule
            $paymentData = [
                'employee_id' => $schedule->employee_id,
                'type' => $schedule->type,
                'amount' => $schedule->amount,
                'payment_method' => 'bank_transfer',
                'payment_date' => now(),
                'notes' => sprintf('Processed from schedule #%d: %s', 
                    $schedule->id, 
                    $schedule->notes ?? ''
                ),
                'schedule_id' => $schedule->id
            ];
            
            $paymentController = new SalaryPaymentController();
            $request = new Request($paymentData);
            
            $response = $paymentController->store($request);
            
            if ($response->getStatusCode() !== 201 && $response->getStatusCode() !== 200) {
                throw new \RuntimeException('Failed to create payment from schedule.');
            }
            
            DB::commit();
            
            Log::info('Schedule processed to payment', [
                'schedule_id' => $schedule->id,
                'payment_id' => $schedule->payment_id,
                'processed_by' => Auth::id()
            ]);
            
            return redirect()->route('salary.payments.index')
                ->with('success', 'Schedule processed successfully and payment has been created.');
                
        } catch (\RuntimeException $e) {
            DB::rollBack();
            
            Log::error('Schedule processing error: ' . $e->getMessage(), [
                'schedule_id' => $schedule->id,
                'user_id' => Auth::id()
            ]);
            
            return back()->with('error', $e->getMessage());
            
        } catch (Throwable $e) {
            DB::rollBack();
            
            Log::error('Unexpected error processing schedule: ' . $e->getMessage(), [
                'schedule_id' => $schedule->id,
                'trace' => $e->getTraceAsString()
            ]);
            
            return back()->with('error', 'An unexpected error occurred while processing the schedule.');
        }
    }
}