<?php

namespace App\Http\Controllers\Traits;

use App\Models\Defect;
use App\Models\DefectImage;
use App\Models\DefectHistory;
use App\Models\EmpCompanyDetails;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Validator;

trait DefectTrait
{
    // ---------- Email helpers ----------
    protected function sendDefectEmailToAssignee($defect, $subjectPrefix = 'Defect Update')
    {
        try {
            $ids = $this->getCustomerAndWorkspaceIds();
            // Collect recipients based on assignment type
            $recipients = [];
            if ($defect->assignment_type === 'internal_employee' && $defect->assigned_emp_id) {
                $emp = \App\Models\EmpCompanyDetails::withoutGlobalScope(\App\Scopes\NotDeletedScope::class)->find($defect->assigned_emp_id);
                if ($emp && !empty($emp->employee_email)) {
                    $nameModel = \App\Models\EmpPersonalDetails::where('emp_id', $defect->assigned_emp_id)->first();
                    $name = $nameModel ? trim(($nameModel->first_name ?? '') . ' ' . ($nameModel->last_name ?? '')) : null;
                    $recipients[] = ['email' => $emp->employee_email, 'name' => $name];
                }
            } elseif ($defect->assignment_type === 'subcontractor' && $defect->subcontractor_id) {
                $sub = \App\Models\LinkManagement::find($defect->subcontractor_id);
                if ($sub && !empty($sub->email)) {
                    $name = $sub->name ?? $sub->company_name ?? null;
                    $recipients[] = ['email' => $sub->email, 'name' => $name];
                }
            } elseif ($defect->assignment_type === 'subcontractor_employee') {
                // Subcontractor
                if ($defect->subcontractor_id) {
                    $sub = \App\Models\LinkManagement::find($defect->subcontractor_id);
                    if ($sub && !empty($sub->email)) {
                        $name = $sub->name ?? $sub->company_name ?? null;
                        $recipients[] = ['email' => $sub->email, 'name' => $name];
                    }
                }
                // Assigned subcontractor employee
                if ($defect->assigned_subcontractor_emp_id) {
                    $emp = \App\Models\EmpCompanyDetails::withoutGlobalScope(\App\Scopes\NotDeletedScope::class)->find($defect->assigned_subcontractor_emp_id);
                    if ($emp && !empty($emp->employee_email)) {
                        $nameModel = \App\Models\EmpPersonalDetails::where('emp_id', $defect->assigned_subcontractor_emp_id)->first();
                        $name = $nameModel ? trim(($nameModel->first_name ?? '') . ' ' . ($nameModel->last_name ?? '')) : null;
                        $recipients[] = ['email' => $emp->employee_email, 'name' => $name];
                    }
                }
            }
            if (empty($recipients)) {
                return; // no email to send
            }
            $statusMap = config('constants.defect_status');
            $statusLabel = collect($statusMap)->firstWhere('key', $defect->status)['value'] ?? 'Unknown';
            $subject = $subjectPrefix . ' | ' . ' - ' . $statusLabel . ' | ' . env('APP_NAME');
            // Prepare email data based on status
            $emailData = [
                'subject' => $subjectPrefix,
                'defect_title' => $defect->title,
                'defect_description' => $defect->description,
                'defect_priority' => $defect->priority,
                'defect_status_label' => $statusLabel,
                'defect_due_date' => $defect->due_date ? \Carbon\Carbon::parse($defect->due_date)->format('d M Y') : null,
                'action_url' => env('FRONTEND_URL') ? rtrim(env('FRONTEND_URL')) : null,
                'customer_id' => $ids['customer_id'] ?? null,
                'reject_reason' => $defect->reject_reason ?? null,
            ];
            // Add status-specific data
            if ($defect->status == config('constants.defect_status.rejected.key')) {
                // When rejected, show reject reason to assignee
                $emailData['reject_reason'] = $defect->reject_reason ?? null;
            }
            foreach ($recipients as $recipient) {
                $emailData['assignee_name'] = $recipient['name'] ?? null;
                $params = [
                    'to' => $recipient['email'],
                    'subject' => $subject,
                    'msg' => view('Emails.defect-assignee', $emailData)->render(),
                ];
                $this->SendInstantEmail($params);
            }
        } catch (\Throwable $e) {
            Log::error('Failed to send defect email to assignee: ' . $e->getMessage());
        }
    }

    protected function sendDefectEmailToCreator($defect, $subjectPrefix = 'Defect Update')
    {
        try {
            $ids = $this->getCustomerAndWorkspaceIds();
            $creatorEmail = null;
            $creatorName = null;
            if ($defect->created_by_type === 'employee') {
                $emp = \App\Models\EmpCompanyDetails::withoutGlobalScope(\App\Scopes\NotDeletedScope::class)->find($defect->created_by);
                $creatorEmail = $emp->employee_email ?? null;
                $nameModel = \App\Models\EmpPersonalDetails::where('emp_id', $defect->created_by)->first();
                $creatorName = $nameModel ? trim(($nameModel->first_name ?? '') . ' ' . ($nameModel->last_name ?? '')) : null;
            } else {
                $user = \App\Models\User::find($defect->created_by);
                $creatorEmail = $user->email ?? null;
                $creatorName = $user->name ?? null;
            }
            if (!$creatorEmail) {
                return;
            }
            $statusMap = config('constants.defect_status');
            $statusLabel = collect($statusMap)->firstWhere('key', $defect->status)['value'] ?? 'Unknown';
            $subject = $subjectPrefix . ' | ' . ' - ' . $statusLabel . ' | ' . env('APP_NAME');
            // Prepare email data based on status
            $emailData = [
                'subject' => $subjectPrefix,
                'creator_name' => $creatorName,
                'defect_title' => $defect->title,
                'defect_description' => $defect->description,
                'defect_priority' => $defect->priority,
                'defect_status_label' => $statusLabel,
                'defect_due_date' => $defect->due_date ? \Carbon\Carbon::parse($defect->due_date)->format('d M Y') : null,
                'action_url' => env('FRONTEND_URL') ? rtrim(env('FRONTEND_URL')) : null,
                'customer_id' => $ids['customer_id'] ?? null,
            ];
            // Add status-specific data
            if ($defect->status == config('constants.defect_status.submitted.key')) {
                // When submitted, show completion description to creator
                $emailData['completion_description'] = $defect->completion_description ?? null;
            } elseif ($defect->status == config('constants.defect_status.rejected.key')) {
                // When rejected, show reject reason to creator
                $emailData['reject_reason'] = $defect->reject_reason ?? null;
            }
            $params = [
                'to' => $creatorEmail,
                'subject' => $subject,
                'msg' => view('Emails.defect-creator', $emailData)->render(),
            ];
            $this->SendInstantEmail($params);
        } catch (\Throwable $e) {
            Log::error('Failed to send defect email to creator: ' . $e->getMessage());
        }
    }
    protected function defectValidation(Request $request, $isUpdate = false)
    {
        $rules = [
            'project_id' => 'nullable|integer|exists:projects,id',
            'site_id' => 'nullable|integer|exists:sites,id',
            'assignment_type' => 'required|in:internal_employee,subcontractor,subcontractor_employee',
            'assigned_emp_id' => 'nullable|integer|exists:emp_company_details,id',
            'subcontractor_id' => 'nullable|integer|exists:link_management,id',
            'assigned_subcontractor_emp_id' => 'nullable|integer|exists:emp_company_details,id',
            'title' => 'required|string|max:255',
            'description' => 'nullable|string',
            'priority' => 'required|in:low,medium,high,critical',
            'due_date' => 'nullable|date|after_or_equal:today',
            'images.*' => 'nullable|image|mimes:jpeg,png,jpg,gif,webp|max:10240',
        ];
        if ($isUpdate) {
            $rules['id'] = 'required|integer|exists:defects,id';
        }
        // Add conditional validation based on assignment type
        $validator = Validator::make($request->all(), $rules);
        $validator->after(function ($validator) use ($request) {
            $assignmentType = $request->assignment_type;
            switch ($assignmentType) {
                case 'internal_employee':
                    if (!$request->assigned_emp_id) {
                        $validator->errors()->add('assigned_emp_id', 'Employee ID is required for internal employee assignment.');
                    }
                    break;
                case 'subcontractor':
                    if (!$request->subcontractor_id) {
                        $validator->errors()->add('subcontractor_id', 'Subcontractor ID is required for subcontractor assignment.');
                    }
                    break;
                case 'subcontractor_employee':
                    if (!$request->subcontractor_id) {
                        $validator->errors()->add('subcontractor_id', 'Subcontractor ID is required for subcontractor employee assignment.');
                    }
                    if (!$request->assigned_subcontractor_emp_id) {
                        $validator->errors()->add('assigned_subcontractor_emp_id', 'Subcontractor employee ID is required for subcontractor employee assignment.');
                    }
                    break;
            }
        });
        return $validator;
    }

    protected function getDefects(Request $request)
    {
        $filters = $request->only(['project_id', 'site_id', 'assigned_emp_id', 'priority', 'status', 'title']);
        $ids = $this->getCustomerAndWorkspaceIds();
        $query = Defect::where('del', '0');
        $query = $this->applyCustomerWorkspaceFilter($query);
        $query = $query->with([
            'project',
            'site',
            'subcontractor',
        ]);
        $user = auth()->user();
        // Check if user is customer or employee
        if ($this->getUserTable() === 'customer') {
            // For customer users: Show all defects under their customer_id (primary key id)
            $query->where('customer_id', $user->id);
        } else {
            $query->where(function ($q) use ($user) {
                $q->where('created_by', $user->id)
                    ->where('created_by_type', 'employee')
                    ->orWhere('assigned_emp_id', $user->id)
                    ->orWhere('assigned_subcontractor_emp_id', $user->id);
            });
        }
        // Apply additional filters
        $query = $query
    
            ->when($filters['project_id'] ?? null, function ($q, $projectId) {
                $q->where('project_id', $projectId);
            })
            ->when($filters['site_id'] ?? null, function ($q, $siteId) {
                $q->where('site_id', $siteId);
            })
            // ->when($filters['assigned_emp_id'] ?? null, function ($q, $empId) {
            //     $q->where('assigned_emp_id', $empId);
            // })
            ->when($filters['priority'] ?? null, function ($q, $priority) {
                $q->where('priority', $priority);
            })
            ->when(isset($filters['status']), function ($q) use ($filters) {
                $q->where('status', $filters['status']);
            })
            ->when($filters['title'] ?? null, function ($q, $title) {
                $q->where('title', 'like', '%' . $title . '%');
            })
            ->orderBy('created_at', 'desc');
        $data = $query->get();
        
        // Get tier permissions for the logged-in user
        $tierPermissions = [];
        
        if ($ids['flag'] === 'emp') {
            $employeeId = Auth::id();
            $employee = \App\Models\EmpCompanyDetails::find($employeeId);
            
            if ($employee) {
                // Get "Site" module permissions using the global function
                $siteModulePermission = $this->getTierPermissionsByModule($employee, 'Sites');
                if ($siteModulePermission) {
                    $tierPermissions = [$siteModulePermission];
                }
            }
        }
        
        // Get the original response structure
        $response = $this->success($data, 'Defects retrieved successfully');
        
        // Add tier_permissions to the response data
        $responseData = $response->getData(true);
        $responseData['tier_permissions'] = $tierPermissions;
        
        return response()->json($responseData, $response->getStatusCode());
    }

    protected function createDefect(Request $request)
    {
        $validator = $this->defectValidation($request);
        if ($validator->fails()) {
            return $this->handleValidationFailure($validator);
        }
        $ids = $this->getCustomerAndWorkspaceIds();
        $data = $validator->validated();
        $data['customer_id'] = $ids['customer_id'];
        $data['workspace_id'] = $ids['workspace_id'];
        // Fix creator information based on user type
        if ($ids['flag'] === 'emp') {
            $data['created_by'] = Auth::id();
            $data['created_by_type'] = 'employee';
        } else {
            // For customer users
            $data['created_by'] = Auth::id();
            $data['created_by_type'] = 'customer';
        }
        $defect = Defect::create($data);
        // Handle image uploads if provided during creation
        if ($request->hasFile('images')) {
            $type = $ids['flag'] === 'emp' ? 'created_by_employee' : 'created_by_customer';
            $uploadedBy = Auth::id();
            foreach ($request->file('images') as $imageFile) {
                if ($imageFile->isValid()) {
                    // Generate unique filename
                    $extension = strtolower($imageFile->getClientOriginalExtension());
                    $originalName = pathinfo($imageFile->getClientOriginalName(), PATHINFO_FILENAME);
                    $sanitizedFileName = preg_replace('/[^a-zA-Z0-9_-]/', '', $originalName);
                    $sanitizedFileName = substr($sanitizedFileName, 0, 100);
                    $uniqueFileName = time() . '_' . uniqid() . '_' . $sanitizedFileName . '.' . $extension;
                    // Create directory if it doesn't exist
                    $directory = public_path('DefectsManagement');
                    if (!file_exists($directory)) {
                        mkdir($directory, 0777, true);
                    }
                    // Move uploaded file
                    $imageFile->move($directory, $uniqueFileName);
                    $imagePath = 'DefectsManagement/' . $uniqueFileName;
                    // Save to database
                    DefectImage::create([
                        'defect_id' => $defect->id,
                        'uploaded_by' => $uploadedBy,
                        'image' => $imagePath,
                        'type' => $type,
                        'customer_id' => $ids['customer_id'],
                        'workspace_id' => $ids['workspace_id'],
                    ]);
                }
            }
        }

        $this->logDefectHistory($defect->id, 'created', 'Defect created', null, $defect->toArray());
        // Email notifications: on create send to assignee
        $this->sendDefectEmailToAssignee($defect, 'New Defect Assigned');
        return $this->success($defect, 'Defect created successfully');
    }

    protected function getDefectById($id, $ids)
    {
        $defect = Defect::with([
            'project',
            'site',
            'employeePersonalDetails',
            'images',
            'histories',
            'subcontractor',
            'assignedSubcontractorEmployeePersonalDetails'
        ])
            ->where('id', $id)
            ->where('customer_id', $ids['customer_id'])
            ->where('workspace_id', $ids['workspace_id'])
            ->first();
        if (!$defect) {
            return $this->error('Defect not found', 404);
        }
        // Add creator information and clean up duplicates
        if ($defect->created_by_type === 'employee') {
            $defect->creator_info = $defect->createdByEmployee;
            $defect->creator_type = 'employee';
            // Remove the duplicate relation data
            unset($defect->created_by_employee);
        } else {
            $defect->creator_info = $defect->createdByCustomer;
            $defect->creator_type = 'customer';
            // Remove the duplicate relation data
            unset($defect->created_by_customer);
        }
        return $this->success($defect, 'Defect retrieved successfully');
    }

    protected function updateDefect(Request $request)
    {
        $validator = $this->defectValidation($request, true);
        if ($validator->fails()) {
            return $this->handleValidationFailure($validator);
        }
        $ids = $this->getCustomerAndWorkspaceIds();
        $data = $validator->validated();
        $defect = Defect::where('id', $data['id'])
            ->where('customer_id', $ids['customer_id'])
            ->where('workspace_id', $ids['workspace_id'])
            ->first();
        if (!$defect) {
            return $this->error('Defect not found', 404);
        }
        $old = $defect->toArray();

        // Don't update created_by and created_by_type on update - keep original values
        unset($data['created_by'], $data['created_by_type']);

        $defect->update($data);

        // Handle image uploads if provided during update
        if ($request->hasFile('images')) {
            // Delete existing images (both from database and physical files)
            $this->deleteExistingDefectImages($defect->id, $ids);
            
            $type = $ids['flag'] === 'emp' ? 'created_by_employee' : 'created_by_customer';
            $uploadedBy = Auth::id();

            foreach ($request->file('images') as $imageFile) {
                if ($imageFile->isValid()) {
                    // Generate unique filename
                    $extension = strtolower($imageFile->getClientOriginalExtension());
                    $originalName = pathinfo($imageFile->getClientOriginalName(), PATHINFO_FILENAME);
                    $sanitizedFileName = preg_replace('/[^a-zA-Z0-9_-]/', '', $originalName);
                    $sanitizedFileName = substr($sanitizedFileName, 0, 100);
                    $uniqueFileName = time() . '_' . uniqid() . '_' . $sanitizedFileName . '.' . $extension;

                    // Create directory if it doesn't exist
                    $directory = public_path('DefectsManagement');
                    if (!file_exists($directory)) {
                        mkdir($directory, 0777, true);
                    }

                    // Move uploaded file
                    $imageFile->move($directory, $uniqueFileName);
                    $imagePath = 'DefectsManagement/' . $uniqueFileName;

                    // Save to database
                    DefectImage::create([
                        'defect_id' => $defect->id,
                        'uploaded_by' => $uploadedBy,
                        'image' => $imagePath,
                        'type' => $type,
                        'customer_id' => $ids['customer_id'],
                        'workspace_id' => $ids['workspace_id'],
                    ]);
                }
            }
        }

        $this->logDefectHistory($defect->id, 'updated', 'Defect updated', $old, $defect->toArray());
        // Notify creator about update
        $this->sendDefectEmailToCreator($defect, 'Defect Updated');
        return $this->success($defect, 'Defect updated successfully');
    }

    protected function deleteDefect($id, $ids)
    {
        $defect = Defect::where('id', $id)
            ->where('customer_id', $ids['customer_id'])
            ->where('workspace_id', $ids['workspace_id'])
            ->where('del', '0')
            ->first();
        if (!$defect) {
            return $this->error('Defect not found', 404);
        }
        $defect->del = 1;
        $defect->save();
        $this->logDefectHistory($defect->id, 'deleted', 'Defect deleted', $defect->toArray(), null);
        return $this->message('Defect deleted successfully');
    }

    // protected function addDefectImages(Request $request)
    // {
    //     $validator = $this->defectAddImageValidation($request);
    //     if ($validator->fails()) {
    //         return $this->handleValidationFailure($validator);
    //     }
    //     $ids = $this->getCustomerAndWorkspaceIds();
    //     $defect = Defect::where('id', $request->defect_id)
    //         ->where('customer_id', $ids['customer_id'])
    //         ->where('workspace_id', $ids['workspace_id'])
    //         ->first();
    //     if (!$defect) {
    //         return $this->error('Defect not found', 404);
    //     }
    //     $type = $ids['flag'] === 'emp' ? 'created_by_employee' : 'created_by_customer';
    //     $uploadedBy = $ids['flag'] === 'emp' ? Auth::id() : 0;
    //     $uploadedPaths = [];
    //     if ($request->hasFile('images')) {
    //         foreach ($request->file('images') as $imageFile) {
    //             if ($imageFile->isValid()) {
    //                 // Generate unique filename
    //                 $extension = strtolower($imageFile->getClientOriginalExtension());
    //                 $originalName = pathinfo($imageFile->getClientOriginalName(), PATHINFO_FILENAME);
    //                 $sanitizedFileName = preg_replace('/[^a-zA-Z0-9_-]/', '', $originalName);
    //                 $sanitizedFileName = substr($sanitizedFileName, 0, 100);
    //                 $uniqueFileName = time() . '_' . uniqid() . '_' . $sanitizedFileName . '.' . $extension;
    //                 // Create directory if it doesn't exist
    //                 $directory = public_path('DefectsManagement');
    //                 if (!file_exists($directory)) {
    //                     mkdir($directory, 0777, true);
    //                 }
    //                 // Move uploaded file
    //                 $imageFile->move($directory, $uniqueFileName);
    //                 $imagePath = 'DefectsManagement/' . $uniqueFileName;
    //                 // Save to database
    //                 $image = DefectImage::create([
    //                     'defect_id' => $defect->id,
    //                     'uploaded_by' => $uploadedBy,
    //                     'image' => $imagePath,
    //                     'type' => $type,
    //                     'customer_id' => $ids['customer_id'],
    //                     'workspace_id' => $ids['workspace_id'],
    //                 ]);
    //                 $uploadedPaths[] = $image;
    //             }
    //         }
    //     }
    //     if (!empty($uploadedPaths)) {
    //         $this->logDefectHistory($defect->id, 'image_added', 'Images added', null, [
    //             'type' => $type,
    //             'count' => count($uploadedPaths)
    //         ]);
    //     }
    //     return $this->message('Images uploaded successfully');
    // }

    protected function deleteDefectImages($id, $ids)
    {
        $defect = DefectImage::where('id', $id)
            ->where('customer_id', $ids['customer_id'])
            ->where('workspace_id', $ids['workspace_id'])
            ->first();
        if (!$defect) {
            return $this->error('Defect image not found', 404);
        }
        if ($defect->defect->status == config('constants.defect_status.submitted.key')) {
            return $this->error('Defect is submitted, cannot delete images', 400);
        }
        if ($defect->defect->status == config('constants.defect_status.approved.key')) {
            return $this->error('Defect is approved, cannot delete images', 400);
        }
        if ($defect->defect->status == config('constants.defect_status.rejected.key')) {
            return $this->error('Defect is rejected, cannot delete images', 400);
        }
        // if ($defect->defect()->status == config('constants.defect_status.completed.key')) {
        //     return $this->error('Defect is completed, cannot delete images', 400);
        // }
        
        // Delete physical file before deleting database record
        $this->deletePhysicalImageFile($defect->image);
        
        $defect->delete();
        return $this->message('Defect image deleted successfully');
    }

    /**
     * Delete all existing images for a defect (both database records and physical files)
     */
    protected function deleteExistingDefectImages($defectId, $ids)
    {
        $existingImages = DefectImage::where('defect_id', $defectId)
            ->where('customer_id', $ids['customer_id'])
            ->where('workspace_id', $ids['workspace_id'])
            ->get();

        foreach ($existingImages as $image) {
            // Delete physical file
            $this->deletePhysicalImageFile($image->image);
            
            // Delete database record
            $image->delete();
        }
    }

    /**
     * Delete physical image file from storage
     */
    protected function deletePhysicalImageFile($imagePath)
    {
        if ($imagePath && file_exists(public_path($imagePath))) {
            unlink(public_path($imagePath));
        }
    }

    protected function submitCompletionDefect(Request $request)
    {
        $validator = Validator::make($request->all(), [
            'id' => 'required|integer|exists:defects,id',
            'completion_description' => 'nullable|string',
            'images.*' => 'nullable|image|mimes:jpeg,png,jpg,gif,webp|max:10240',
        ]);
        if ($validator->fails()) {
            return $this->handleValidationFailure($validator);
        }
        $ids = $this->getCustomerAndWorkspaceIds();
        $defect = Defect::where('id', $request->id)
        ->where('customer_id', $ids['customer_id'])
        ->where('workspace_id', $ids['workspace_id'])
        ->where('status', config('constants.defect_status.in_progress.key')) // in_progress
        ->where(function ($q) {
            $q->where('assigned_emp_id', Auth::id())
            ->orWhere('assigned_subcontractor_emp_id', Auth::id())
            ->orWhere('created_by', Auth::id());
        })
        ->first();
        if (!$defect) {
            return $this->error('Defect not found', 404);
        }
        $old = $defect->toArray();
        $defect->completion_description = $request->completion_description;
        $defect->status = config('constants.defect_status.submitted.key'); // submitted
        $defect->completed_at = now();
        $defect->save();
        if ($request->hasFile('images')) {
            foreach ($request->file('images') as $imageFile) {
                if ($imageFile->isValid()) {
                    // Generate unique filename
                    $extension = strtolower($imageFile->getClientOriginalExtension());
                    $originalName = pathinfo($imageFile->getClientOriginalName(), PATHINFO_FILENAME);
                    $sanitizedFileName = preg_replace('/[^a-zA-Z0-9_-]/', '', $originalName);
                    $sanitizedFileName = substr($sanitizedFileName, 0, 100);
                    $uniqueFileName = time() . '_' . uniqid() . '_' . $sanitizedFileName . '.' . $extension;

                    // Create directory if it doesn't exist
                    $directory = public_path('DefectsManagement');
                    if (!file_exists($directory)) {
                        mkdir($directory, 0777, true);
                    }

                    // Move uploaded file
                    $imageFile->move($directory, $uniqueFileName);
                    $imagePath = 'DefectsManagement/' . $uniqueFileName;

                    DefectImage::create([
                        'defect_id' => $defect->id,
                        'uploaded_by' => Auth::id(),
                        'image' => $imagePath,
                        'type' => 'assigned_employee',
                        'customer_id' => $ids['customer_id'],
                        'workspace_id' => $ids['workspace_id'],
                    ]);
                }
            }
        }
        $this->logDefectHistory($defect->id, 'submitted', 'Task submitted by employee', $old, $defect->toArray());
        // Email: notify creator on submitted
        $this->sendDefectEmailToCreator($defect, 'Defect Submitted');

        return $this->success($defect, 'Task submitted successfully');
    }

    protected function approveDefect(Request $request)
    {
        $validator = Validator::make($request->all(), [
            'id' => 'required|integer|exists:defects,id',
        ]);
        if ($validator->fails()) {
            return $this->handleValidationFailure($validator);
        }
        $ids = $this->getCustomerAndWorkspaceIds();
        $defect = Defect::where('id', $request->id)
            ->where('customer_id', $ids['customer_id'])
            ->where('workspace_id', $ids['workspace_id'])
            ->first();
        if (!$defect) {
            return $this->error('Defect not found', 404);
        }
        $old = $defect->toArray();
        $defect->status = config('constants.defect_status.completed.key'); // approved
        $defect->reject_reason = null;
        $defect->save();
        $this->logDefectHistory($defect->id, 'completed', 'Defect completed', $old, $defect->toArray());
        // Email: notify assignee on approved
        $this->sendDefectEmailToAssignee($defect, 'Defect Approved');
        return $this->success($defect, 'Defect approved successfully');
    }

    protected function rejectDefect(Request $request)
    {
        $validator = Validator::make($request->all(), [
            'id' => 'required|integer|exists:defects,id',
            'reject_reason' => 'required|string',
            'images.*' => 'nullable|image|mimes:jpeg,png,jpg,gif,webp|max:10240',
        ]);
        if ($validator->fails()) {
            return $this->handleValidationFailure($validator);
        }
        $ids = $this->getCustomerAndWorkspaceIds();
        $defect = Defect::where('id', $request->id)
            ->where('customer_id', $ids['customer_id'])
            ->where('workspace_id', $ids['workspace_id'])
            ->first();
        if (!$defect) {
            return $this->error('Defect not found', 404);
        }
        $old = $defect->toArray();
        $defect->status = config('constants.defect_status.in_progress.key');
        $defect->reject_reason = $request->reject_reason;
        $type = $ids['flag'] === 'emp' ? 'created_by_employee' : 'created_by_customer';
        if ($request->hasFile('images')) {
            foreach ($request->file('images') as $imageFile) {
                if ($imageFile->isValid()) {
                    // Generate unique filename
                    $extension = strtolower($imageFile->getClientOriginalExtension());
                    $originalName = pathinfo($imageFile->getClientOriginalName(), PATHINFO_FILENAME);
                    $sanitizedFileName = preg_replace('/[^a-zA-Z0-9_-]/', '', $originalName);
                    $sanitizedFileName = substr($sanitizedFileName, 0, 100);
                    $uniqueFileName = time() . '_' . uniqid() . '_' . $sanitizedFileName . '.' . $extension;
                    // Create directory if it doesn't exist
                    $directory = public_path('DefectsManagement');
                    if (!file_exists($directory)) {
                        mkdir($directory, 0777, true);
                    }
                    // Move uploaded file
                    $imageFile->move($directory, $uniqueFileName);
                    $imagePath = 'DefectsManagement/' . $uniqueFileName;
                    DefectImage::create([
                        'defect_id' => $defect->id,
                        'uploaded_by' => Auth::id(),
                        'image' => $imagePath,
                        'type' => $type,
                        'customer_id' => $ids['customer_id'],
                        'workspace_id' => $ids['workspace_id'],
                    ]);
                }
            }
        }
        $defect->save();
        $this->logDefectHistory($defect->id, 'rejected', 'Defect rejected', $old, $defect->toArray());
        // Email: notify creator on rejected
        $this->sendDefectEmailToAssignee($defect, 'Defect Rejected');
        return $this->success($defect, 'Defect rejected with reason');
    }

    // protected function reassignDefect(Request $request)
    // {
    //     $validator = Validator::make($request->all(), [
    //         'id' => 'required|integer|exists:defects,id',
    //         'assignment_type' => 'required|in:internal_employee,subcontractor,subcontractor_employee',
    //         'assigned_emp_id' => 'nullable|integer|exists:emp_company_details,id',
    //         'subcontractor_id' => 'nullable|integer|exists:companies,id',
    //         'assigned_subcontractor_emp_id' => 'nullable|integer|exists:emp_company_details,id',
    //     ]);
    //     // Add conditional validation based on assignment type
    //     $validator->after(function ($validator) use ($request) {
    //         $assignmentType = $request->assignment_type;

    //         switch ($assignmentType) {
    //             case 'internal_employee':
    //                 if (!$request->assigned_emp_id) {
    //                     $validator->errors()->add('assigned_emp_id', 'Employee ID is required for internal employee assignment.');
    //                 }
    //                 break;
    //             case 'subcontractor':
    //                 if (!$request->subcontractor_id) {
    //                     $validator->errors()->add('subcontractor_id', 'Subcontractor ID is required for subcontractor assignment.');
    //                 }
    //                 break;
    //             case 'subcontractor_employee':
    //                 if (!$request->subcontractor_id) {
    //                     $validator->errors()->add('subcontractor_id', 'Subcontractor ID is required for subcontractor employee assignment.');
    //                 }
    //                 if (!$request->assigned_subcontractor_emp_id) {
    //                     $validator->errors()->add('assigned_subcontractor_emp_id', 'Subcontractor employee ID is required for subcontractor employee assignment.');
    //                 }
    //                 break;
    //         }
    //     });
    //     if ($validator->fails()) {
    //         return $this->handleValidationFailure($validator);
    //     }
    //     $ids = $this->getCustomerAndWorkspaceIds();
    //     $defect = Defect::where('id', $request->id)
    //         ->where('customer_id', $ids['customer_id'])
    //         ->where('workspace_id', $ids['workspace_id'])
    //         ->first();
    //     if (!$defect) {
    //         return $this->error('Defect not found', 404);
    //     }
    //     $old = $defect->toArray();
    //     // Update assignment based on type
    //     $defect->assignment_type = $request->assignment_type;
    //     $defect->assigned_emp_id = $request->assigned_emp_id;
    //     $defect->subcontractor_id = $request->subcontractor_id;
    //     $defect->assigned_subcontractor_emp_id = $request->assigned_subcontractor_emp_id;
    //     $defect->status = config('constants.defect_status.reassigned.key'); // reassigned
    //     $defect->save();
    //     $assigneeInfo = $this->getAssigneeInfo($request->assignment_type, $request);
    //     $this->logDefectHistory($defect->id, 'reassigned', 'Defect reassigned to ' . $assigneeInfo, $old, $defect->toArray());
    //     // Email: notify new assignee
    //     $this->sendDefectEmailToAssignee($defect, 'Defect Reassigned');
    //     return $this->success($defect, 'Defect reassigned successfully');
    // }


    protected function getDefectHistory($id)
    {
        $ids = $this->getCustomerAndWorkspaceIds();
        $defect = Defect::where('id', $id)
            ->where('customer_id', $ids['customer_id'])
            ->where('workspace_id', $ids['workspace_id'])
            ->first();
        if (!$defect) {
            return $this->error('Defect not found', 404);
        }
        $history = DefectHistory::where('defect_id', $defect->id)
            ->orderBy('created_at', 'desc')
            ->get();
        return $this->success($history, 'History retrieved successfully');
    }

    protected function logDefectHistory($defectId, $action, $description = null, $old = null, $new = null)
    {
        try {
            $ids = $this->getCustomerAndWorkspaceIds();
            DefectHistory::create([
                'defect_id' => $defectId,
                'action' => $action,
                'description' => $description,
                'changed_by' => Auth::id(),
                'changed_by_type' => $this->getUserTable() === 'customer' ? 'customer' : 'employee',
                'old_value' => $old,
                'new_value' => $new,
                'customer_id' => $ids['customer_id'] ?? null,
                'workspace_id' => $ids['workspace_id'] ?? null,
            ]);
        } catch (\Exception $e) {
            Log::error('Failed to log defect history: ' . $e->getMessage());
        }
    }

    protected function getAssignedDefectsForEmployee()
    {
        $ids = $this->getCustomerAndWorkspaceIds();
        $defects = Defect::with(['project', 'site', 'employeePersonalDetails', 'images', 'histories'])
            ->where('assigned_emp_id', Auth::id())
            ->where('customer_id', $ids['customer_id'])
            ->where('workspace_id', $ids['workspace_id'])
            ->where('del', '0')
            ->get();
        $defects->transform(function ($defect) {
            if ($defect->created_by_type === 'employee') {
                $defect->creator_info = $defect->createdByEmployee;
                $defect->creator_type = 'employee';
                // Remove the duplicate relation data
                unset($defect->created_by_employee);
            } else {
                $defect->creator_info = $defect->createdByCustomer;
                $defect->creator_type = 'customer';
                // Remove the duplicate relation data
                unset($defect->created_by_customer);
            }
            return $defect;
        });
        return $this->success($defects, 'Assigned defects retrieved successfully');
    }

    protected function markInProgressDefect(Request $request)
    {
        $validator = Validator::make($request->all(), [
            'id' => 'required|integer|exists:defects,id',
        ]);
        if ($validator->fails()) {
            return $this->handleValidationFailure($validator);
        }
        $ids = $this->getCustomerAndWorkspaceIds();
        $defect = Defect::where('id', $request->id)
            ->where('customer_id', $ids['customer_id'])
            ->where('workspace_id', $ids['workspace_id'])
            ->where('status', config('constants.defect_status.open.key'))
            ->where(function ($q) {
                $q->where('assigned_emp_id', Auth::id())
                    ->orWhere('assigned_subcontractor_emp_id', Auth::id())
                    ->orWhere('created_by', Auth::id());
            })
            ->first();
        if (!$defect) {
            return $this->error('Defect not found', 404);
        }
        $old = $defect->toArray();
        $defect->status = config('constants.defect_status.in_progress.key'); // in progress
        $defect->save();
        $this->logDefectHistory($defect->id, 'in_progress', 'Defect marked in progress', $old, $defect->toArray());
        // Email: notify creator when in progress
        $this->sendDefectEmailToCreator($defect, 'Defect In Progress');
        return $this->success($defect, 'Defect marked in progress successfully');
    }

    // Helper method to get assignee information for logging
    protected function getAssigneeInfo($assignmentType, $request)
    {
        switch ($assignmentType) {
            case 'internal_employee':
                return 'Internal Employee ID: ' . $request->assigned_emp_id;
            case 'subcontractor':
                return 'Subcontractor ID: ' . $request->subcontractor_id;
            case 'subcontractor_employee':
                return 'Subcontractor ID: ' . $request->subcontractor_id . ', Employee ID: ' . $request->assigned_subcontractor_emp_id;
            default:
                return 'Unknown assignment type';
        }
    }

    // Get assignable internal employees
    protected function getAssignableInternalEmployees()
    {
        $ids = $this->getCustomerAndWorkspaceIds();
        $employees = EmpCompanyDetails::with('employeePersonalDetails')
            ->where('customer_id', $ids['customer_id'])
            ->where('workspace_id', $ids['workspace_id'])
            ->where('del', '0')
            ->get();
        return $this->success($employees, 'Internal employees retrieved successfully');
    }

    // Get assignable subcontractors
    protected function getAssignableSubcontractors()
    {
        $ids = $this->getCustomerAndWorkspaceIds();
        $subcontractors = \App\Models\LinkManagement::where('customer_id', $ids['customer_id'])
            ->where('workspace_id', $ids['workspace_id'])
            ->where('status', 1)
            ->get(['id', 'name', 'email', 'phone', 'company_name']);
        return $this->success($subcontractors, 'Subcontractors retrieved successfully');
    }

    // Get assignable subcontractor employees
    protected function getAssignableSubcontractorEmployees($subcontractorId = null)
    {
        $ids = $this->getCustomerAndWorkspaceIds();
        $query = EmpCompanyDetails::with('employeePersonalDetails')
            ->where('customer_id', $ids['customer_id'])
            ->where('workspace_id', $ids['workspace_id'])
            ->where('del', '0');
        if ($subcontractorId) {
            $query->where('company_id', $subcontractorId);
        }
        $employees = $query->get();
        return $this->success($employees, 'Subcontractor employees retrieved successfully');
    }

    // Get all assignable entities (for dropdown/selection purposes)
    protected function getAllAssignableEntities()
    {
        $ids = $this->getCustomerAndWorkspaceIds();
        $internalEmployees = EmpCompanyDetails::with('employeePersonalDetails')
            ->where('customer_id', $ids['customer_id'])
            ->where('workspace_id', $ids['workspace_id'])
            ->where('del', '0')
            ->get();
        $subcontractors = \App\Models\LinkManagement::where('customer_id', $ids['customer_id'])
            ->where('workspace_id', $ids['workspace_id'])
            ->where('status', 1)
            ->get(['id', 'name', 'email', 'phone', 'company_name']);
        $data = [
            'internal_employees' => $internalEmployees,
            'subcontractors' => $subcontractors
        ];
        return $this->success($data, 'All assignable entities retrieved successfully');
    }
}
