<?php

namespace App\Controllers;

use App\Models\ClaimModel;
use App\Models\ClaimFilesModel;
use App\Models\PolicyModel;
use App\Models\EnquiryModel;
use App\Models\QuotationModel;
use App\Models\ClaimStatusModel;
use CodeIgniter\RESTful\ResourceController;

class ClaimController extends ResourceController
{
    protected $db;
    protected $ClaimModel;
    protected $ClaimFilesModel;
    protected $PolicyModel;
    protected $QuotationModel;
    protected $EnquiryModel;
    protected $ClaimStatusModel;

    private const CLAIM_UPLOAD_DIR = 'uploads/claims/';
    private const CLAIM_TICKET_TYPE = 8;

    public function __construct()
    {
        $this->db = db_connect();
        $this->ClaimModel = new ClaimModel();
        $this->ClaimFilesModel = new ClaimFilesModel();
        $this->PolicyModel = new PolicyModel();
        $this->QuotationModel = new QuotationModel();
        $this->EnquiryModel = new EnquiryModel();
        $this->ClaimStatusModel = new ClaimStatusModel();
    }

    public function claimStatusList()
    {
        try {
            $data = $this->ClaimStatusModel
                ->select('id, claim_status, allowed_status, ticket_type, trigger_type')
                ->where('ticket_type', self::CLAIM_TICKET_TYPE)
                ->where('is_active', 1)
                ->orderBy('id', 'ASC')
                ->findAll();

            return $this->respond(['status' => 'success', 'code' => 200, 'data' => $data], 200);
        } catch (\Exception $e) {
            return $this->respond(['status' => 'failed', 'code' => 500, 'data' => $e->getMessage()], 500);
        }
    }

    public function ClaimList()
    {
        try {
            $id              = $this->request->getGet('id');
            $manager_id      = $this->request->getGet('manager_id');
            $agent_id        = $this->request->getGet('agent_id');
            $policy_number   = $this->request->getGet('policy_number');
            $claim_type_id   = $this->request->getGet('claim_type_id');
            $claim_status_id = $this->request->getGet('claim_status_id');
            $insurer_id      = $this->request->getGet('insurer_id');
            $from_date       = $this->request->getGet('from_date');
            $to_date         = $this->request->getGet('to_date');

            $builder = $this->db->table('partner_claims pc')
                ->select("pc.*,
                        i.name as insurer_name,
                        i.short_name as insurer_short_name,
                        pct.claim_type as claim_type_value,
                        pa.name as agent_name,
                        pb.name as broker_name,
                        tcs.claim_status as claim_status_value,
                        pp.start_date as policy_start_date,
                        pp.end_date as policy_end_date,
                        pp.rc_no as reg_no,
                        CASE 
                            WHEN pc.reg_no IS NULL OR pc.reg_no = '' THEN pp.rc_no 
                            ELSE pc.reg_no 
                        END AS reg_no
                    ")
                ->join('insurers i', 'i.id = pc.insurer_id', 'left')
                ->join('partner_claim_type_master pct', 'pct.id = pc.claim_type', 'left')
                ->join('partner_agent pa', 'pa.id = pc.agent_id', 'left')
                ->join('partner_brokers pb', 'pb.id = pc.broker_id', 'left')
                ->join('ticket_claim_status tcs', 'tcs.id = pc.claim_status_id AND tcs.ticket_type = ' . self::CLAIM_TICKET_TYPE, 'left')
                ->join('partner_policy pp', 'pp.id = pc.policy_id', 'left')
                ->join('vehicle v', 'v.id = pp.vehicle_id', 'left')
                ->where('pc.is_active', 1);

            if (!empty($id)) {
                $builder->where('pc.id', $id);
                $claim = $builder->get()->getRowArray();

                if (!$claim) {
                    return $this->respond(['status' => 'failed', 'code' => 404, 'data' => 'Not Found'], 404);
                }

                $claim = $this->formatClaimRow($claim);
                $claim['files'] = $this->decorateFilesWithUrls($this->getClaimFiles((int) $claim['id']));

                return $this->respond(['status' => 'success', 'code' => 200, 'data' => $claim], 200);
            }

            if (!empty($manager_id)) {
                $builder->where('pc.manager_id', $manager_id);
            }

            if (!empty($agent_id)) {
                $builder->where('pc.agent_id', $agent_id);
            }

            if (!empty($policy_number)) {
                $builder->where('pc.policy_no', $policy_number);
            }

            if (!empty($claim_type_id)) {
                $builder->where('pc.claim_type', $claim_type_id);
            }

            if (!empty($claim_status_id)) {
                $builder->where('pc.claim_status_id', $claim_status_id);
            }

            if (!empty($insurer_id)) {
                $builder->where('pc.insurer_id', $insurer_id);
            }

            if (!empty($from_date) && !empty($to_date)) {
                $builder->where('pc.created_at >=', $from_date . ' 00:00:00')
                    ->where('pc.created_at <=', $to_date . ' 23:59:59');
            }

            $data = $builder->orderBy('pc.id', 'DESC')->get()->getResultArray();
            $filesByClaim = $this->getClaimFilesGrouped(array_column($data, 'id'));

            foreach ($data as $key => $row) {
                $data[$key] = $this->formatClaimRow($row);
                $data[$key]['files'] = $this->decorateFilesWithUrls($filesByClaim[$row['id']] ?? []);
            }

            return $this->respond(['status' => 'success', 'code' => 200, 'data' => $data], 200);
        } catch (\Exception $e) {
            return $this->respond(['status' => 'failed', 'code' => 500, 'data' => $e->getMessage()], 500);
        }
    }

    public function createClaim()
    {
        try {
            $reqData = $this->request->getPost();

            if (empty($reqData['policy_number']) || empty($reqData['claim_type']) || empty($reqData['policy_from'])) {
                return $this->respond([
                    'status' => 'failed',
                    'code'   => 400,
                    'data'   => 'policy_number, claim_type and policy_from are required',
                ], 400);
            }

            if($reqData['policy_from'] === 'Internal') {
                $policy = $this->PolicyModel
                    ->select('partner_policy.*, Q.insurer_id, E.mobile as client_mobile, E.email as client_email, E.broker_id')
                    ->join('partner_quotation Q', 'Q.id = partner_policy.quotation_id AND Q.status = "Accepted"', 'left')
                    ->join('partner_enquiry E', 'E.id = partner_policy.enquiry_id', 'left')
                    ->where('partner_policy.policy_number', $reqData['policy_number'])
                    ->first();

                if (empty($policy)) {
                    return $this->respond(['status' => 'failed', 'code' => 404, 'data' => 'Policy not found'], 404);
                }
            }

            $status = $this->ClaimStatusModel
                ->where('ticket_type', self::CLAIM_TICKET_TYPE)
                ->where('is_active', 1)
                ->orderBy('id', 'ASC')
                ->first();

            if (empty($status)) {
                return $this->respond(['status' => 'failed', 'code' => 404, 'data' => 'Default claim status not found'], 404);
            }

            $claimData = [
                'claim_status_id'          => $reqData['claim_status_id'] ?? $status['id'],
                'policy_id'                => $policy['id'] ?? null,
                'policy_no'                => $policy['policy_number'] ?? $reqData['policy_number'] ?? null,
                'claim_number'             => $reqData['claim_number'] ?? null,
                'policy_from'              => $reqData['policy_from'],
                'insurer_id'               => $policy['insurer_id'] ?? $reqData['insurer_id'] ?? null,
                'broker_id'                => $reqData['broker_id'] ?? $policy['broker_id'] ?? null,
                'insured_name'             => $reqData['insured_name'] ?? $policy['insured_name'] ?? null,
                'mobile'                   => $reqData['mobile'] ?? $policy['client_mobile'] ?? null,
                'mail'                     => $reqData['mail'] ?? $policy['client_email'] ?? null,
                'manager_id'               => $policy['manager_id'] ?? $reqData['manager_id'] ?? null,
                'agent_id'                 => $policy['agent_id'] ?? $reqData['agent_id'] ?? null,
                'claim_type'               => $reqData['claim_type'],
                'date_of_incident'         => format_date_for_database($reqData['date_of_incident'] ?? null),
                'place_of_incident'        => $reqData['place_of_incident'] ?? null,
                'claim_description'        => $reqData['claim_description'] ?? null,
                'spot_surveyor_name'       => $reqData['spot_surveyor_name'] ?? null,
                'spot_surveyor_contact'    => $reqData['spot_surveyor_contact'] ?? null,
                'workshop_surveyor_name'   => $reqData['workshop_surveyor_name'] ?? null,
                'workshop_surveyor_contact'=> $reqData['workshop_surveyor_contact'] ?? null,
                'remark'                   => $reqData['remark'] ?? null,
                'is_active'                => 1,
                'created_by'               => $reqData['created_by'] ?? null,
                'reg_no'                   => $reqData['reg_no'] ?? null,
            ];

            $this->db->transStart();

            if (!$this->ClaimModel->insert($claimData)) {
                $this->db->transRollback();
                return $this->respond(['status' => 'failed', 'code' => 422, 'data' => $this->ClaimModel->errors()], 422);
            }

            $claimId = (int) $this->ClaimModel->getInsertID();
            $fileResult = $this->saveClaimFiles($claimId, $reqData['created_by'] ?? null);

            if ($fileResult['attempted'] > 0 && empty($fileResult['saved'])) {
                $this->db->transRollback();
                return $this->respond([
                    'status' => 'failed',
                    'code'   => 422,
                    'data'   => !empty($fileResult['errors']) ? $fileResult['errors'] : 'Failed to upload claim files',
                ], 422);
            }

            if ($this->db->transStatus() === false) {
                return $this->respond(['status' => 'failed', 'code' => 500, 'data' => 'Failed to create claim'], 500);
            }

            $this->db->transComplete();

            return $this->respond([
                'status' => 'success',
                'code'   => 200,
                'data'   => [
                    'claim_id'       => $claimId,
                    'uploaded_files' => $fileResult['saved'],
                    'files'          => $this->decorateFilesWithUrls($this->getClaimFiles($claimId)),
                ],
            ], 200);
        } catch (\Exception $e) {
            return $this->respond(['status' => 'failed', 'code' => 500, 'data' => $e->getMessage()], 500);
        }
    }

    public function updateClaim()
    {
        try {
            $claimId = $this->request->getPost('id');

            if (empty($claimId)) {
                return $this->respond(['status' => 'failed', 'code' => 400, 'data' => 'id is required'], 400);
            }

            $reqData = $this->request->getPost();
            $claim = $this->ClaimModel->where('is_active', 1)->find((int) $claimId);

            if (!$claim) {
                return $this->respond(['status' => 'failed', 'code' => 404, 'data' => 'Claim not found'], 404);
            }

            $updateData = [];
            $allowedUpdateFields = [
                'claim_status_id',
                'insurer_id',
                'broker_id',
                'policy_id',
                'policy_no',
                'insured_name',
                'mobile',
                'mail',
                'manager_id',
                'agent_id',
                'claim_type',
                'place_of_incident',
                'claim_description',
                'spot_surveyor_name',
                'spot_surveyor_contact',
                'workshop_surveyor_name',
                'workshop_surveyor_contact',
                'remark',
                'is_active',
                'policy_from',
            ];

            foreach ($allowedUpdateFields as $field) {
                if (array_key_exists($field, $reqData)) {
                    $updateData[$field] = $reqData[$field];
                }
            }

            if (array_key_exists('date_of_incident', $reqData)) {
                $updateData['date_of_incident'] = format_date_for_database($reqData['date_of_incident']);
            }

            if (!empty($reqData['policy_number'])) {
                $policy = $this->PolicyModel
                    ->select('partner_policy.*, Q.insurer_id')
                    ->join('partner_quotation Q', 'Q.id = partner_policy.quotation_id', 'left')
                    ->where('partner_policy.policy_number', $reqData['policy_number'])
                    ->first();

                if ($policy) {
                    $updateData['policy_id'] = $policy['id'];
                    $updateData['policy_no'] = $policy['policy_number'];
                    if (!array_key_exists('insurer_id', $updateData)) {
                        $updateData['insurer_id'] = $policy['insurer_id'] ?? null;
                    }
                }
            }

            if (isset($reqData['updated_by'])) {
                $updateData['updated_by'] = $reqData['updated_by'];
            }

            if($reqData['policy_from'] === 'External') {
                $updateData['reg_no'] = $reqData['reg_no'] ?? null;
                $updateData['claim_number'] = $reqData['claim_number'] ?? null;
                $updateData['policy_no'] = $reqData['policy_number'] ?? null;
            }

            if (!empty($updateData) && !$this->ClaimModel->update((int) $claimId, $updateData)) {
                return $this->respond(['status' => 'failed', 'code' => 422, 'data' => $this->ClaimModel->errors()], 422);
            }

            $fileResult = $this->saveClaimFiles((int) $claimId, $reqData['updated_by'] ?? $reqData['created_by'] ?? null);

            if ($fileResult['attempted'] > 0 && empty($fileResult['saved'])) {
                return $this->respond([
                    'status' => 'failed',
                    'code'   => 422,
                    'data'   => !empty($fileResult['errors']) ? $fileResult['errors'] : 'Failed to upload claim files',
                ], 422);
            }

            return $this->respond([
                'status' => 'success',
                'code'   => 200,
                'data'   => [
                    'claim_id'       => (int) $claimId,
                    'uploaded_files' => $fileResult['saved'],
                    'files'          => $this->decorateFilesWithUrls($this->getClaimFiles((int) $claimId)),
                ],
            ], 200);
        } catch (\Exception $e) {
            return $this->respond(['status' => 'failed', 'code' => 500, 'data' => $e->getMessage()], 500);
        }
    }

    /**
     * Fetch a single claim by primary id with related lookups and file preview URLs.
     * Used to populate the edit form when the user clicks the edit button on a row.
     */
    public function getClaimById()
    {
        try {
            $id = $this->request->getGet('id');

            if (empty($id)) {
                return $this->respond(['status' => 'failed', 'code' => 400, 'data' => 'id is required'], 400);
            }

            $claim = $this->db->table('partner_claims pc')
                ->select("pc.*,
                        i.name as insurer_name,
                        i.short_name as insurer_short_name,
                        pct.claim_type as claim_type_value,
                        pa.name as agent_name,
                        pb.name as broker_name,
                        tcs.claim_status as claim_status_value,
                        pp.start_date as policy_start_date,
                        pp.end_date as policy_end_date,

                        CASE 
                            WHEN pc.reg_no IS NULL OR pc.reg_no = '' THEN v.vehicle_no 
                            ELSE pc.reg_no 
                        END AS reg_no
                    ")
                ->join('insurers i', 'i.id = pc.insurer_id', 'left')
                ->join('partner_claim_type_master pct', 'pct.id = pc.claim_type', 'left')
                ->join('partner_agent pa', 'pa.id = pc.agent_id', 'left')
                ->join('partner_brokers pb', 'pb.id = pc.broker_id', 'left')
                ->join('ticket_claim_status tcs', 'tcs.id = pc.claim_status_id AND tcs.ticket_type = ' . self::CLAIM_TICKET_TYPE, 'left')
                ->join('partner_policy pp', 'pp.id = pc.policy_id', 'left')
                ->join('vehicle v', 'v.id = pp.vehicle_id', 'left')
                ->where('pc.id', (int) $id)
                ->where('pc.is_active', 1)
                ->get()
                ->getRowArray();

            if (!$claim) {
                return $this->respond(['status' => 'failed', 'code' => 404, 'data' => 'Claim not found'], 404);
            }

            $claim = $this->formatClaimRow($claim);
            $claim['files'] = $this->decorateFilesWithUrls($this->getClaimFiles((int) $claim['id']));

            return $this->respond(['status' => 'success', 'code' => 200, 'data' => $claim], 200);
        } catch (\Exception $e) {
            return $this->respond(['status' => 'failed', 'code' => 500, 'data' => $e->getMessage()], 500);
        }
    }

    /**
     * Stream a single claim file inline (used as preview / download URL).
     */
    public function downloadClaimFile()
    {
        try {
            $fileId = $this->request->getGet('file_id');

            if (empty($fileId)) {
                return $this->respond(['status' => 'failed', 'code' => 400, 'data' => 'file_id is required'], 400);
            }

            $file = $this->ClaimFilesModel
                ->where('id', (int) $fileId)
                ->where('is_active', 1)
                ->first();

            if (!$file) {
                return $this->respond(['status' => 'failed', 'code' => 404, 'data' => 'File not found'], 404);
            }

            $relativePath = ltrim((string) ($file['file_path'] ?? ''), '/');
            $filePath     = WRITEPATH . $relativePath;

            if (empty($relativePath) || !file_exists($filePath)) {
                return $this->respond(['status' => 'failed', 'code' => 404, 'data' => 'File missing on server'], 404);
            }

            $mime         = $file['file_mime_type'] ?? mime_content_type($filePath) ?? 'application/octet-stream';
            $downloadName = $file['file_name'] ?: basename($filePath);

            return $this->response
                ->setHeader('Content-Type', $mime)
                ->setHeader('Content-Disposition', 'inline; filename="' . $downloadName . '"')
                ->setBody(file_get_contents($filePath));
        } catch (\Exception $e) {
            return $this->respond(['status' => 'failed', 'code' => 500, 'data' => $e->getMessage()], 500);
        }
    }

    private function formatClaimRow(array $row): array
    {
        if (!empty($row['created_at'])) {
            $row['created_at'] = date('d-m-Y h:i A', strtotime($row['created_at']));
        }

        if (!empty($row['updated_at'])) {
            $row['updated_at'] = date('d-m-Y h:i A', strtotime($row['updated_at']));
        }

        if (!empty($row['date_of_incident']) && $row['date_of_incident'] !== '0000-00-00') {
            $row['date_of_incident'] = date('d-m-Y', strtotime($row['date_of_incident']));
        } else {
            $row['date_of_incident'] = null;
        }

        $row['policy_start_date'] = (!empty($row['policy_start_date']) && $row['policy_start_date'] !== '0000-00-00')
            ? date('d-m-Y', strtotime($row['policy_start_date']))
            : null;

        $row['policy_end_date'] = (!empty($row['policy_end_date']) && $row['policy_end_date'] !== '0000-00-00')
            ? date('d-m-Y', strtotime($row['policy_end_date']))
            : null;

        return $row;
    }

    private function getClaimFiles(int $claimId): array
    {
        return $this->ClaimFilesModel
            ->where('claim_id', $claimId)
            ->where('is_active', 1)
            ->orderBy('id', 'ASC')
            ->findAll();
    }

    /**
     * Add preview_url and download_url to each claim file row.
     */
    private function decorateFilesWithUrls(array $files): array
    {
        foreach ($files as $key => $file) {
            $fileId = $file['id'] ?? null;
            $url    = $fileId ? base_url('api/claim/downloadClaimFile?file_id=' . $fileId) : null;

            $files[$key]['preview_url']  = $url;
            $files[$key]['download_url'] = $url;
        }

        return $files;
    }

    private function getClaimFilesGrouped(array $claimIds): array
    {
        $claimIds = array_values(array_filter(array_map('intval', $claimIds)));

        if (empty($claimIds)) {
            return [];
        }

        $files = $this->ClaimFilesModel
            ->whereIn('claim_id', $claimIds)
            ->where('is_active', 1)
            ->orderBy('id', 'ASC')
            ->findAll();

        $grouped = [];
        foreach ($files as $file) {
            $grouped[$file['claim_id']][] = $file;
        }

        return $grouped;
    }

    /**
     * Collect uploaded files from claim_files field (single or multiple).
     */
    private function collectClaimUploadFiles(): array
    {
        $filesToProcess = [];
    
        // getFileMultiple() works when input name is claim_files[]
        $multiple = $this->request->getFileMultiple('claim_files');
    
        if (!empty($multiple) && is_array($multiple)) {
            foreach ($multiple as $uploadedFile) {
                if ($this->isValidUploadFile($uploadedFile)) {
                    $filesToProcess[] = $uploadedFile;
                }
            }
            return $filesToProcess;
        }
    
        // Fallback: getFile() may return an array of files in some CI4 versions
        $single = $this->request->getFile('claim_files');
    
        if (is_array($single)) {
            foreach ($single as $uploadedFile) {
                if ($this->isValidUploadFile($uploadedFile)) {
                    $filesToProcess[] = $uploadedFile;
                }
            }
        } elseif ($this->isValidUploadFile($single)) {
            $filesToProcess[] = $single;
        }
    
        return $filesToProcess;
    }

    private function isValidUploadFile($uploadedFile): bool
    {
        return $uploadedFile
            && method_exists($uploadedFile, 'isValid')
            && $uploadedFile->isValid()
            && !$uploadedFile->hasMoved();
    }

    private function saveClaimFiles(int $claimId, $createdBy = null): array
    {
        $uploadPath = WRITEPATH . self::CLAIM_UPLOAD_DIR;
        if (!is_dir($uploadPath)) {
            mkdir($uploadPath, 0777, true);
        }

        $result = [
            'saved'     => [],
            'errors'    => [],
            'attempted' => 0,
        ];

        $filesToProcess = $this->collectClaimUploadFiles();
        $result['attempted'] = count($filesToProcess);

        foreach ($filesToProcess as $file) {
            try {
                $storedName = time() . '_' . $file->getRandomName();

                if (!$file->move($uploadPath, $storedName)) {
                    $result['errors'][] = $file->getErrorString() ?: 'Unable to move uploaded file';
                    continue;
                }

                if (!$this->ClaimFilesModel->insert([
                    'claim_id'       => $claimId,
                    'file_name'      => $file->getClientName() ?: $storedName,
                    'file_path'      => self::CLAIM_UPLOAD_DIR . $storedName,
                    'file_extension' => $file->getClientExtension(),
                    'file_mime_type' => $file->getClientMimeType(),
                    'is_active'      => 1,
                    'created_by'     => $createdBy,
                ])) {
                    if (is_file($uploadPath . $storedName)) {
                        unlink($uploadPath . $storedName);
                    }
                    $result['errors'][] = $this->ClaimFilesModel->errors() ?: 'Failed to save file record';
                    continue;
                }

                $result['saved'][] = $storedName;
            } catch (\Exception $e) {
                $result['errors'][] = $e->getMessage();
            }
        }

        return $result;
    }
}
