<?php

namespace App\Http\Controllers;

use Carbon\Carbon;
use App\Models\Overtime;
use App\Models\Sites;
use App\Models\EmpTeamsMember;
use App\Models\EmpPersonalDetails;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Log;

class OvertimeController extends Controller
{


    public function index(Request $request)
    {

        $validator = $this->overtimeFilterValidationRequest($request);
        if ($validator->fails()) {
            return $this->handleValidationFailure($validator);
        }
        $overtimesQuery = Overtime::with(['sites', 'emppersonaldetails'])->latest('id');
        $overtimesQuery = $this->applyCustomerWorkspaceFilter($overtimesQuery);
        // Apply search filter
        if ($request->filled('search')) {
            $searchTerm = $request->search;
            $overtimesQuery->where(function ($q) use ($searchTerm) {
                $q->where('description', 'like', '%' . $searchTerm . '%')
                    // Apply name search using helper
                    ->orWhere(function ($nameQuery) use ($searchTerm) {
                        $this->applyNameSearch($nameQuery, $searchTerm, 'emppersonaldetails');
                    })
                    ->orWhereHas('sites', function ($subquery) use ($searchTerm) {
                        $subquery->where('title', 'like', '%' . $searchTerm . '%');
                    });
            });
        }
        // Apply customer and workspace filters
        return $this->withCount($overtimesQuery, 'Overtime records retrieved successfully');
    }

    /**
     * Store a new overtime record
     */
    public function store(Request $request)
    {
        try {
            $validator = $this->overtimeStoreValidationRequest($request);
            if ($validator->fails()) {
                return $this->handleValidationFailure($validator);
            }
            $ids = $this->getCustomerAndWorkspaceIds();
            $validatedData = $validator->validated();
            // Check for existing overlapping overtime
            if ($this->hasOverlappingOvertime($validatedData)) {
                return $this->error('An overtime record with overlapping times already exists for this employee', 400);
            }
            // Calculate working hours
            $workingHours = $this->calculateWorkingHours($validatedData['check_in'], $validatedData['check_out']);
            $overtime = Overtime::create([
                'employee_id' => $validatedData['employee_id'],
                'site_id' => $validatedData['site_id'],
                'check_in' => $validatedData['check_in'],
                'check_out' => $validatedData['check_out'],
                'date' => $validatedData['date'],
                'working_hours' => $workingHours,
                'description' => $validatedData['description'],
                'status' => $validatedData['status'] ?? config('constants.overtime_status.pending'),
                'customer_id' => $ids['customer_id'],
                'workspace_id' => $ids['workspace_id'],
            ]);

            // Send notifications for overtime application if status is pending
            if (($validatedData['status'] ?? config('constants.overtime_status.pending')) === config('constants.overtime_status.pending')) {
                $this->sendOvertimeNotifications($overtime, $ids);
            }

            return $this->success($overtime, 'Overtime record created successfully');
        } catch (\Exception $e) {
            Log::error('Overtime store error: ' . $e->getMessage());
            return $this->error('Failed to create overtime record', 500);
        }
    }

    /**
     * Show a specific overtime record
     */
    public function show($id)
    {
        try {
            $overtime = Overtime::find($id);
            if (!$overtime) {
                return $this->error('Overtime record not found', 404);
            }
            // Check customer/workspace access
            $ids = $this->getCustomerAndWorkspaceIds();
            if ($overtime->customer_id !== $ids['customer_id'] || $overtime->workspace_id !== $ids['workspace_id']) {
                return $this->error('You do not have access to this overtime record', 403);
            }
            return $this->success($overtime, 'Overtime record retrieved successfully');
        } catch (\Exception $e) {
            Log::error('Overtime show error: ' . $e->getMessage());
            return $this->error('Failed to retrieve overtime record', 500);
        }
    }

    /**
     * Update an existing overtime record
     */
    public function update(Request $request)
    {
        try {
            $validator = $this->overtimeUpdateValidationRequest($request);
            if ($validator->fails()) {
                return $this->handleValidationFailure($validator);
            }
            $overtime = Overtime::find($request->id);
            if (!$overtime) {
                return $this->error('Overtime record not found', 404);
            }
            // Check customer/workspace access
            $ids = $this->getCustomerAndWorkspaceIds();
            if ($overtime->customer_id !== $ids['customer_id'] || $overtime->workspace_id !== $ids['workspace_id']) {
                return $this->error('You do not have access to this overtime record', 403);
            }
            $validatedData = $validator->validated();
            // Check for existing overlapping overtime (excluding current record)
            if ($this->hasOverlappingOvertime($validatedData, $request->id)) {
                return $this->error('An overtime record with overlapping times already exists for this employee', 400);
            }
            // Calculate working hours
            $workingHours = $this->calculateWorkingHours($validatedData['check_in'], $validatedData['check_out']);
            $validatedData['working_hours'] = $workingHours;
            $overtime->update($validatedData);
            return $this->success($overtime, 'Overtime record updated successfully');
        } catch (\Exception $e) {
            Log::error('Overtime update error: ' . $e->getMessage());
            return $this->error('Failed to update overtime record', 500);
        }
    }

    /**
     * Update overtime status
     */
    public function updateStatus(Request $request)
    {
        try {
            $validator = $this->overtimeStatusValidationRequest($request);
            if ($validator->fails()) {
                return $this->handleValidationFailure($validator);
            }
            $overtime = Overtime::find($request->id);
            if (!$overtime) {
                return $this->error('Overtime record not found', 404);
            }
            // Check customer/workspace access
            $ids = $this->getCustomerAndWorkspaceIds();
            if ($overtime->customer_id !== $ids['customer_id'] || $overtime->workspace_id !== $ids['workspace_id']) {
                return $this->error('You do not have access to this overtime record', 403);
            }
            $oldStatus = $overtime->status;
            $overtime->update(['status' => $request->status]);

            // Send notification for status change
            if ($oldStatus !== $request->status) {
                $this->sendOvertimeStatusChangeNotification($overtime, $oldStatus, $request->status, $ids);
            }

            return $this->success($overtime, 'Overtime status updated successfully');
        } catch (\Exception $e) {
            Log::error('Overtime status update error: ' . $e->getMessage());
            return $this->error('Failed to update overtime status', 500);
        }
    }

    /**
     * Delete an overtime record
     */
    public function destroy($id)
    {
        try {
            $overtime = Overtime::query();
            $overtime = $this->applyCustomerWorkspaceFilter($overtime);
            $overtime = $overtime->find($id);
            if (!$overtime) {
                return $this->error('Overtime record not found', 404);
            }
            $overtime->delete();
            return $this->message('Overtime record deleted successfully');
        } catch (\Exception $e) {
            Log::error('Overtime delete error: ' . $e->getMessage());
            return $this->error('Failed to delete overtime record', 500);
        }
    }

    /**
     * Apply for overtime (employee self-service)
     */
    public function applyOvertime(Request $request)
    {
        try {
            $validator = $this->overtimeApplicationValidationRequest($request);
            if ($validator->fails()) {
                return $this->handleValidationFailure($validator);
            }
            $ids = $this->getCustomerAndWorkspaceIds();
            if (!$ids) {
                return $this->error('Unauthorized access', 403);
            }
            $validatedData = $validator->validated();
            $validatedData['employee_id'] = Auth::id();
            // Check for existing overlapping overtime
            if ($this->hasOverlappingOvertime($validatedData)) {
                return $this->error('An overtime record with overlapping times already exists', 400);
            }
            // Calculate working hours
            $workingHours = $this->calculateWorkingHours($validatedData['check_in'], $validatedData['check_out']);
            $overtime = Overtime::create([
                'employee_id' => $validatedData['employee_id'],
                'site_id' => $validatedData['site_id'],
                'check_in' => $validatedData['check_in'],
                'check_out' => $validatedData['check_out'],
                'date' => $validatedData['date'],
                'working_hours' => $workingHours,
                'description' => $validatedData['description'],
                'status' => config('constants.overtime_status.pending'),
                'customer_id' => $ids['customer_id'],
                'workspace_id' => $ids['workspace_id'],
            ]);

            // Send notifications for overtime application
            $this->sendOvertimeNotifications($overtime, $ids);

            return $this->success($overtime, 'Overtime application submitted successfully');
        } catch (\Exception $e) {
            Log::error('Overtime application error: ' . $e->getMessage());
            return $this->error('Failed to submit overtime application', 500);
        }
    }

    /**
     * Bulk delete overtime records
     */
    public function bulkDelete(Request $request)
    {
        try {
            $validator = $this->overtimeBulkDeleteValidationRequest($request);
            if ($validator->fails()) {
                return $this->handleValidationFailure($validator);
            }
            $deletedCount = Overtime::query();
            $deletedCount = $this->applyCustomerWorkspaceFilter($deletedCount);
            $deletedCount = $deletedCount->whereIn('id', $request->ids)->delete();
            return $this->success(['deleted_count' => $deletedCount], 'Overtime records deleted successfully');
        } catch (\Exception $e) {
            Log::error('Overtime bulk delete error: ' . $e->getMessage());
            return $this->error('Failed to delete overtime records', 500);
        }
    }

    /**
     * Check for overlapping overtime records
     */
    private function hasOverlappingOvertime($data, $excludeId = null)
    {
        $query = Overtime::where('employee_id', $data['employee_id'])
            ->where(function ($query) use ($data) {
                $query->whereBetween('check_in', [$data['check_in'], $data['check_out']])
                    ->orWhereBetween('check_out', [$data['check_in'], $data['check_out']])
                    ->orWhere(function ($query) use ($data) {
                        $query->where('check_in', '<', $data['check_in'])
                            ->where('check_out', '>', $data['check_out']);
                    });
            });
        if ($excludeId) {
            $query->where('id', '!=', $excludeId);
        }
        return $query->exists();
    }

    /**
     * Calculate working hours in minutes
     */
    private function calculateWorkingHours($checkIn, $checkOut)
    {
        return Carbon::parse($checkIn)->diffInMinutes(Carbon::parse($checkOut));
    }

    /**
     * Apply name filter to query
     */
    private function applyNameFilter($query, $name)
    {
        $nameComponents = explode(' ', $name);

        $query->whereHas('emppersonaldetails', function ($subquery) use ($nameComponents) {
            $subquery->where(function ($q) use ($nameComponents) {
                foreach ($nameComponents as $component) {
                    $q->orWhere('first_name', 'like', '%' . $component . '%')
                        ->orWhere('middle_name', 'like', '%' . $component . '%')
                        ->orWhere('last_name', 'like', '%' . $component . '%');
                }
            });
        });
    }

    /**
     * Send notifications for overtime application
     */
    private function sendOvertimeNotifications($overtime, $ids)
    {

        try {
            // Get employee details
            $employee = EmpPersonalDetails::where('emp_id', $overtime->employee_id)->first();
            if (!$employee) {
                Log::warning('Employee not found for overtime notification: ' . $overtime->employee_id);
                return;
            }

            // Get site details
            $site = Sites::find($overtime->site_id);
            if (!$site) {
                Log::warning('Site not found for overtime notification: ' . $overtime->site_id);
                return;
            }
            // Prepare notification message
            $employeeName = trim($employee->first_name . ' ' . $employee->middle_name . ' ' . $employee->last_name);
            $date = Carbon::parse($overtime->date)->format('d/m/Y');
            // Get raw database values to avoid accessor formatting issues
            $rawCheckIn = $overtime->getRawOriginal('check_in');
            $rawCheckOut = $overtime->getRawOriginal('check_out');

            // Debug: Log the raw values to see what's in the database
            Log::info('Overtime notification - Raw check_in from DB: ' . $rawCheckIn);
            Log::info('Overtime notification - Raw check_out from DB: ' . $rawCheckOut);

            // Extract time from datetime values
            $checkIn = $this->getTimeFromDatetimeHelper($rawCheckIn);
            $checkOut = $this->getTimeFromDatetimeHelper($rawCheckOut);

            // Debug: Log the extracted time values
            Log::info('Overtime notification - Extracted check_in time: ' . $checkIn);
            Log::info('Overtime notification - Extracted check_out time: ' . $checkOut);
            $workingHours = round($overtime->working_hours / 60, 2);
            $notificationTitle = 'Overtime Application Submitted';
            $notificationMessage = "Employee {$employeeName} has submitted an overtime application for {$date} from {$checkIn} to {$checkOut} ({$workingHours} hours) at {$site->title}.";

            // Send notification to customer (customer_id represents the customer who owns the workspace)
            // The customer will receive notification about overtime applications from their employees
            $this->save_notifications(
                $notificationTitle,
                $notificationMessage,
                config('constants.employee_types.employee'),
                $overtime->employee_id, // Sender is the employee
                config('constants.employee_types.customer'),
                $ids['customer_id'],    // Receiver is the customer
                config('constants.notification_types.overtime_applied'),
                $ids['customer_id'],
                $ids['workspace_id']
            );
        } catch (\Exception $e) {
            Log::error('Error sending overtime notifications: ' . $e->getMessage());
        }
    }

    function getTimeFromDatetimeHelper($datetime)
    {
        if (!$datetime) {
            return '00:00:00';
        }

        try {
            // Try using Carbon for more robust parsing
            $carbon = Carbon::parse($datetime);
            return $carbon->format('H:i:s');
        } catch (\Exception $e) {
            // Fallback to strtotime if Carbon fails
            try {
                $timestamp = strtotime($datetime);
                if ($timestamp !== false) {
                    return date('H:i:s', $timestamp);
                }
            } catch (\Exception $e2) {
                Log::warning("Failed to parse datetime in getTimeFromDatetimeHelper: " . $e2->getMessage());
            }

            // If all parsing fails, return default
            return '00:00:00';
        }
    }

    /**
     * Send notification when overtime status changes
     */
    private function sendOvertimeStatusChangeNotification($overtime, $oldStatus, $newStatus, $ids)
    {
        try {
            $employee = EmpPersonalDetails::where('emp_id', $overtime->employee_id)->first();
            if (!$employee) {
                return;
            }

            $employeeName = trim($employee->first_name . ' ' . $employee->middle_name . ' ' . $employee->last_name);
            $date = Carbon::parse($overtime->date)->format('d/m/Y');

            $statusText = ucfirst($newStatus);
            $notificationTitle = "Overtime {$statusText}";
            $notificationMessage = "Your overtime application for {$date} has been {$newStatus}.";

            // Send notification to employee
            $this->save_notifications(
                $notificationTitle,
                $notificationMessage,
                config('constants.employee_types.customer'),
                Auth::id(), // Sender is the approver/rejector
                config('constants.employee_types.employee'),
                $overtime->employee_id,
                config('constants.notification_types.overtime_' . $newStatus),
                $ids['customer_id'],
                $ids['workspace_id']
            );
        } catch (\Exception $e) {
            Log::error('Error sending overtime status change notification: ' . $e->getMessage());
        }
    }
}
