<?php



namespace Modules\Passanger\Controllers\Api;



use App\Controllers\BaseController;

use Modules\User\Models\UserModel;

use Modules\User\Models\UserDetailModel;

use Modules\User\Models\UserDeletionReasonModel;

use Modules\User\Models\UserDeletionModel;

use Modules\Role\Models\RoleModel;

use Modules\Ticket\Models\TicketModel;

use Modules\Schedule\Models\ScheduleModel;

use Modules\Trip\Models\TripModel;

use Modules\Rating\Models\RatingModel;

use Modules\Passanger\Models\Socialsignin;

use Modules\Passanger\Models\UserDeviceModel;

use Modules\Trip\Models\SubtripModel;

use Modules\Ticket\Models\CancelModel;

use Modules\Ticket\Models\JourneylistModel;

use Modules\Cancellation\Models\GlobalCancellationModel;

use App\Libraries\TicketCancellationSupport;

use App\Libraries\FCMService;

use App\Libraries\FirebaseNotification;





use CodeIgniter\API\ResponseTrait;

use Firebase\JWT\JWT;

use Exception;

use App\Libraries\Tokenjwt;



class Passanger extends BaseController

{

    use ResponseTrait;

    protected $Viewpath;

    protected $userModel;

    protected $userDetailModel;

    protected $roleModel;

    protected $tokenJwt;

    protected $ticketModel;

    protected $subtripModel;

    protected $cancelModel;

    protected $journeylistModel;



    protected $scheduleModel;

    protected $tripModel;



    protected $ratingModel;



    protected $socialsigninModel;

    protected $UserDeviceModel;

    protected $FCMService;

    protected $FirebaseNotification;
    
    protected $userDeletionReasonModel;

    protected $userDeletionModel;

    protected $db;

    protected $globalCancellationModel;





    public function __construct()

    {



        $this->Viewpath = "Modules\Passanger\Views";

        $this->userModel = new UserModel();

        $this->userDetailModel = new UserDetailModel();

        $this->roleModel = new RoleModel();

        $this->db = \Config\Database::connect();



        $this->tokenJwt = new Tokenjwt();

        $this->ticketModel = new TicketModel();

        $this->globalCancellationModel = new GlobalCancellationModel();



        $this->scheduleModel = new ScheduleModel();

        $this->tripModel = new TripModel();



        $this->ratingModel = new RatingModel();



        $this->socialsigninModel = new Socialsignin();

        $this->subtripModel = new SubtripModel();

        $this->cancelModel = new CancelModel();

        $this->journeylistModel = new JourneylistModel();

        $this->userdeviceModel = new UserDeviceModel();

        $this->FCMService = new FCMService();

        $this->FirebaseNotification = new FirebaseNotification();
        
        $this->userDeletionReasonModel = new UserDeletionReasonModel();

        $this->userDeletionModel = new UserDeletionModel();

    }

    public function getPassangerdata($segment, $type)



    {

        $userdata = array();

        if ($type == "email") {

            $userdetail = $this->userModel->join('user_details', 'user_details.user_id = users.id', 'left')->where('role_id', 3)->where('status', 1)->where('login_email', $segment)->findAll();

        }

        if ($type == "mobile") {

            $userdetail = $this->userModel->join('user_details', 'user_details.user_id = users.id', 'left')->where('role_id', 3)->where('status', 1)->where('login_mobile', $segment)->findAll();

        }



        if (empty($userdetail)) {

            $data = [

                'message' => "No Data not found.",

                'status' => "fail",

                'response' => 204,



            ];

            return $this->response->setJSON($data);

        } else {

            foreach ($userdetail as $key => $uservalue) {

                $userdata['user_id'] = $uservalue->user_id;

                $userdata['login_email'] = $uservalue->login_email;

                $userdata['login_mobile'] = $uservalue->login_mobile;

                $userdata['slug'] = $uservalue->slug;

                $userdata['status'] = $uservalue->status;

                $userdata['first_name'] = $uservalue->first_name;

                $userdata['last_name'] = $uservalue->last_name;

                $userdata['id_number'] = $uservalue->id_number;

                $userdata['id_type'] = $uservalue->id_type;

                $userdata['address'] = $uservalue->address;

                $userdata['country_id'] = $uservalue->country_id;

                $userdata['city'] = $uservalue->city;

                $userdata['zip_code'] = $uservalue->zip_code;

            }

            $data = [

                'status' => "success",

                'response' => 200,

                'data' => $userdata,

            ];



            return $this->response->setJSON($data);

        }

    }



    public function getPassanger()

    {

        $segment = trim($this->request->getVar('userid') ?? '');

        $password = $this->request->getVar('password');

        $type = strtolower(trim($this->request->getVar('type') ?? ''));


        if (empty($segment)) {

            return $this->response->setJSON([

                'status' => "fail",

                'response' => 400,

                'message' => "User ID is required"

            ]);

        }


        // Build query to find user
        // We use withDeleted() to check if user was soft-deleted
        $query = $this->userModel->select('users.*, user_details.first_name, user_details.last_name')
                                ->join('user_details', 'user_details.user_id = users.id', 'left');


        if ($type == "email") {

            $query->where('users.login_email', $segment);

        } elseif ($type == "mobile") {

            $query->where('users.login_mobile', $segment);

        } else {

            // Fallback: search both if type is unknown
            $query->groupStart()

                  ->where('users.login_email', $segment)

                  ->orWhere('users.login_mobile', $segment)

                  ->groupEnd();

        }


        $userdetail = $query->withDeleted()->first();


        if ($userdetail) {

            // Check if user is soft-deleted
            if ($userdetail->deleted_at !== null) {

                return $this->response->setJSON([

                    'status' => "fail",

                    'response' => 204,

                    'message' => "Account has been deleted"

                ]);

            }


            // Check role (Passenger role ID is 3)
            if ($userdetail->role_id != 3) {

                return $this->response->setJSON([

                    'status' => "fail",

                    'response' => 204,

                    'message' => "This account is not a passenger account"

                ]);

            }


            // Check status (Active status is 1)
            if ($userdetail->status != 1) {

                return $this->response->setJSON([

                    'status' => "fail",

                    'response' => 204,

                    'message' => "Account is currently inactive"

                ]);

            }


            // Verify password
            $pass = $userdetail->password;

            $verify_pass = password_verify($password, $pass);


            if ($verify_pass) {

                $token = $this->tokenJwt->generateToken($userdetail->slug);


                $data = [

                    'status' => "success",

                    'response' => 200,

                    'data' => $token,

                    'user_id' => $userdetail->id,

                ];


                return $this->response->setJSON($data);

            } else {

                $data = [

                    'message' => "Password Not Match",

                    'status' => "fail",

                    'response' => 204,

                ];


                return $this->response->setJSON($data);

            }

        } else {

            $data = [

                'message' => "User Name Not Match",

                'status' => "fail",

                'response' => 204,

            ];


            return $this->response->setJSON($data);

        }

    }







    public function getPassangerinfo()



    {

        $key = getenv('TOKEN_SECRET');

        $token = $this->tokenJwt->tokencheck();









        try {

            $decoded = JWT::decode($token, $key, array("HS256"));







            $userdetail = $this->userModel->join('user_details', 'user_details.user_id = users.id', 'left')->where('role_id', 3)->where('status', 1)->where('slug', $decoded->slug)->findAll();



            foreach ($userdetail as $key => $uservalue) {

                $userdata['user_id'] = $uservalue->user_id;

                $userdata['login_email'] = $uservalue->login_email;

                $userdata['login_mobile'] = $uservalue->login_mobile;

                $userdata['slug'] = $uservalue->slug;

                $userdata['status'] = $uservalue->status;

                $userdata['first_name'] = $uservalue->first_name;

                $userdata['last_name'] = $uservalue->last_name;

                $userdata['id_number'] = $uservalue->id_number;

                $userdata['id_type'] = $uservalue->id_type;

                $userdata['address'] = $uservalue->address;

                $userdata['country_id'] = $uservalue->country_id;

                $userdata['city'] = $uservalue->city;

                $userdata['zip_code'] = $uservalue->zip_code;

                if (!empty($uservalue->image)) {

                    $userdata['image'] = base_url() . '/public/' . $uservalue->image;

                }

            }



            $data = [

                'status' => "success",

                'response' => 200,

                'data' => $userdata,

            ];



            return $this->response->setJSON($data);

        } catch (Exception $ex) {

            $data = [

                'status' => "fail",

                'response' => 201,

                'data' => "token not valid",

            ];

            return $this->response->setJSON($data);

        }

    }





    /**
     * Rows from global_cancellation_policies (admin: modules/backend/cancellations/global).
     *
     * @return list<array{id:int,hours:int,charges:float}>
     */
    protected function getGlobalCancellationPolicyForApi(): array
    {
        $rows = $this->globalCancellationModel
            ->select('id, hours, charges')
            ->orderBy('hours', 'ASC')
            ->findAll();

        return array_map(static function ($row) {
            return [
                'id'      => (int) $row->id,
                'hours'   => (int) $row->hours,
                'charges' => (float) $row->charges,
            ];
        }, $rows);
    }

    /**
     * Opt-in on GET tickets only. Default false so existing app/website calls are unchanged.
     */
    protected function shouldIncludeCancellationPreview(): bool
    {
        $raw = $this->request->getGet('include_cancellation_preview');
        if ($raw === null || $raw === '') {
            return false;
        }

        return filter_var($raw, FILTER_VALIDATE_BOOLEAN);
    }

    public function getTickets()

    {

        $key = getenv('TOKEN_SECRET');

        $token = $this->tokenJwt->tokencheck();



        try {

            // check and validate user token

            $decoded = JWT::decode($token, $key, array("HS256"));

            $userdetail = $this->userModel->where('slug', $decoded->slug)->first();

            $includeCancellationPreview = $this->shouldIncludeCancellationPreview();

            // get tickets

            $ticketlist = $this->ticketModel

                // select columns

                ->select('tickets.*')

                ->select('(SELECT c.status FROM cancels c WHERE c.booking_id = tickets.booking_id AND c.type = ' . $this->db->escape('ticket') . ' ORDER BY c.id DESC LIMIT 1) AS online_cancel_refund_status', false)

                ->select('(SELECT c.refund_amount FROM cancels c WHERE c.booking_id = tickets.booking_id AND c.type = ' . $this->db->escape('ticket') . ' ORDER BY c.id DESC LIMIT 1) AS online_cancel_refund_amount', false)

                ->select('l1.name AS pick_location_name, l2.name AS drop_location_name')

                ->select('s1.name AS pick_stand_name, s2.name AS drop_stand_name')

                ->select('companies.name AS company_name, companies.logo AS company_logo')



                // join with locations

                ->join('locations l1', 'tickets.pick_location_id = l1.id', 'left')

                ->join('locations l2', 'tickets.drop_location_id = l2.id', 'left')

                ->join('pickdrops pd1', 'tickets.pick_stand_id = pd1.id', 'left')

                ->join('pickdrops pd2', 'tickets.drop_stand_id = pd2.id', 'left')

                ->join('stands s1', 'pd1.stand_id = s1.id', 'left')

                ->join('stands s2', 'pd2.stand_id = s2.id', 'left')

                ->join('trips', 'tickets.trip_id = trips.id', 'left')

                ->join('companies', 'trips.company_id = companies.id', 'left')



                // select rows

                ->where('passanger_id', $userdetail->id)

                ->orderBy('tickets.id', 'DESC')

                ->findAll();


            if (empty($ticketlist)) {

                // ticket list is empty

                $data = [

                    'status' => "fail",

                    'response' => 201,

                    'data' => "No ticket found",

                    'global_cancellation_policy' => $globalCancellationPolicy,

                    'cancellation_policy_help' => $this->getCancellationPolicyHelp(),

                ];

                return $this->response->setJSON($data);

            }

            $classData = [];

            $this->db->transStart();

            $allSeatClass = $this->db->table('seat_class')->get()->getResult();

            $this->db->transComplete();

            foreach ($allSeatClass as $class) {

                $classData[$class->id] = $class;

            }

            $journeyDateHelper = new \App\Libraries\TripJourneyDateHelper();

            foreach ($ticketlist as $key => $ticketvalue) {

                $tipdetil = $this->tripModel->where('id', $ticketvalue->trip_id)->first();
                
                if(!empty($tipdetil)){
                    $scheduldetail = $this->scheduleModel->where('id', $tipdetil->schedule_id)->first();
                }
                



                $reviewStatus = 0;

                $rating = $this->ratingModel->where('booking_id', $ticketvalue->booking_id)->first();



                if (!empty($rating)) {

                    $reviewStatus = 1;

                }

                $subtripInformation = $this->subtripModel->find($ticketvalue->subtrip_id);

                if(isset($subtripInformation->subtrip_seatclass) && !empty($subtripInformation->subtrip_seatclass)){

                    $seatclass = $subtripInformation->subtrip_seatclass ?? [];

                }else{

                    $seatclass = $company_name->seatclass ??[];

                }

                $seatclass = is_string($seatclass) ? json_decode($seatclass, true) : $seatclass;



                $seatwithClass = [];



                if (!empty($ticketvalue->seatnumber)) {

                    $seatNumbers = explode(',', $ticketvalue->seatnumber); // example: "1,2,3"



                    foreach ($seatNumbers as $seat) {

                        $seat = trim($seat);

                        $classFound = null;
                        
                        // Find seat class for this seat

                        // foreach ($seatclass as $sc) {

                        //     if (in_array($seat, $sc['seatNo'])) {

                        //         $classFound = $sc['seatClass'];

                        //         break;

                        //     }

                        // }
                        // Ensure $seatclass is always an array
                        if (!is_array($seatclass)) {
                            $seatclass = json_decode(json_encode($seatclass), true);
                        }
                        
                        // Or if it might be null
                        $seatclass = $seatclass ?? [];

                        foreach ($seatclass as $sc) {
                            if (in_array((int)$seat, array_map('intval', $sc['seatNo']), true)) {
                                $classFound = $sc['seatClass'];
                                break;
                            }
                        }




                        $seatwithClass[] = [

                            "seatnumber" => $seat,

                            "seatclass"  => $classFound ? $classData[$classFound]->name :'Economy', // fallback if no match

                        ];

                    }

                }



                $ticketdata[$key]['id'] = $ticketvalue->id;

                $ticketdata[$key]['booking_id'] = $ticketvalue->booking_id;

                $ticketdata[$key]['trip_id'] = $ticketvalue->trip_id;

                $ticketdata[$key]['subtrip_id'] = $ticketvalue->subtrip_id;

                $ticketdata[$key]['passanger_id'] = $ticketvalue->passanger_id;

                $ticketdata[$key]['pick_location_id'] = $ticketvalue->pick_location_id;

                $ticketdata[$key]['pick_location_name'] = $ticketvalue->pick_location_name;

                $ticketdata[$key]['drop_location_id'] = $ticketvalue->drop_location_id;

                $ticketdata[$key]['drop_location_name'] = $ticketvalue->drop_location_name;

                $ticketdata[$key]['pick_stand_id'] = $ticketvalue->pick_stand_id;

                $ticketdata[$key]['pick_stand_name'] = $ticketvalue->pick_stand_name;

                $ticketdata[$key]['drop_stand_id'] = $ticketvalue->drop_stand_id;

                $ticketdata[$key]['drop_stand_name'] = $ticketvalue->drop_stand_name;

                $ticketdata[$key]['price'] = $ticketvalue->price;

                $ticketdata[$key]['discount'] = $ticketvalue->discount;

                $ticketdata[$key]['totaltax'] = $ticketvalue->totaltax;

                $ticketdata[$key]['paidamount'] = $ticketvalue->paidamount;

                $ticketdata[$key]['offerer'] = $ticketvalue->offerer;

                $ticketdata[$key]['adult'] = $ticketvalue->adult;

                $ticketdata[$key]['chield'] = $ticketvalue->chield;

                $ticketdata[$key]['special'] = $ticketvalue->special;

                $ticketdata[$key]['seatnumber'] = $ticketvalue->seatnumber;

                $ticketdata[$key]['totalseat'] = $ticketvalue->totalseat;

                $boardingDate = $journeyDateHelper->getPassengerBoardingDateForDisplay(
                    (int) $ticketvalue->trip_id,
                    (int) $ticketvalue->subtrip_id,
                    $ticketvalue->journeydata,
                    (int) ($ticketvalue->pick_stand_id ?? 0)
                );
                $ticketdata[$key]['journeydata'] = $boardingDate;
                $ticketdata[$key]['boarding_date'] = $boardingDate;

                $ticketdata[$key]['payment_status'] = $ticketvalue->payment_status;

                $ticketdata[$key]['vehicle_id'] = $ticketvalue->vehicle_id;

                $ticketdata[$key]['payment_detail'] = $ticketvalue->payment_detail;

                $ticketdata[$key]['startime'] = $scheduldetail->start_time ?? "";

                $ticketdata[$key]['endtime'] = $scheduldetail->end_time ?? "";

                $ticketdata[$key]['refund'] = $ticketvalue->refund;

                $ticketdata[$key]['cancel_status'] = $ticketvalue->cancel_status;

                $ticketdata[$key]['review_status'] = $reviewStatus;

                $ticketdata[$key]['booking_date'] = $ticketvalue->created_at;

                $ticketdata[$key]['seat_class'] = $seatwithClass;

                $ticketdata[$key]['company_name'] = $ticketvalue->company_name;

                $ticketdata[$key]['company_logo'] = !empty($ticketvalue->company_logo) ? base_url() . '/public/' . $ticketvalue->company_logo : "";

                $rs = $ticketvalue->online_cancel_refund_status ?? null;
                if ($rs === 'complete') {
                    $rs = 'done';
                }
                $ticketdata[$key]['refund_status'] = $rs;

                $ticketdata[$key]['refund_amount'] = isset($ticketvalue->online_cancel_refund_amount) ? (float) $ticketvalue->online_cancel_refund_amount : null;

                if ($includeCancellationPreview) {
                    $ticketdata[$key]['cancellation_preview'] = TicketCancellationSupport::buildPassengerCancellationPreview((int) $ticketvalue->id);
                }

            }



            $data = [

                'status' => "success",

                'response' => 200,

                'data' => $ticketdata,

            ];

            if ($includeCancellationPreview) {
                $data['global_cancellation_policy'] = $this->getGlobalCancellationPolicyForApi();
                $data['cancellation_policy_help'] = $this->getCancellationPolicyHelp();
                $data['cancellation_integration'] = TicketCancellationSupport::getCancellationIntegrationGuide();
            }

            return $this->response->setJSON($data);

        } catch (Exception $ex) {

            $data = [

                'status' => "fail",

                'response' => 201,

                'data' => "token not valid",

            ];

            return $this->response->setJSON($data);

        }

    }



    public function passangerpicuplod()

    {

        $path = 'image/passenger';

        $image =  $this->request->getFile('image');



        $validation =     $this->validate([

            'image' => 'uploaded[image]|max_size[image,1024]',

        ]);

        if (!$validation) {



            $data = [

                'status' => "fail",

                'response' => 201,

                'data' => $validation,

                'message' => "Max file size 1MB",

            ];



            return $this->response->setJSON($data);

        }





        if ($image->isValid() && !$image->hasMoved()) {

            $profilepic     = $this->imgaeCheck($image, $path);

        }



        $key = getenv('TOKEN_SECRET');

        $token = $this->tokenJwt->tokencheck();











        try {

            $decoded = JWT::decode($token, $key, array("HS256"));





            $userdetailId = $this->usercheck($decoded->slug);



            $picupload = array(

                "id" => $userdetailId->id,

                "image" => $profilepic,



            );



            $success = $this->userDetailModel->save($picupload);



            $data = [

                'status' => "success",

                'response' => 200,

                'data' => $success,

            ];



            return $this->response->setJSON($data);

        } catch (Exception $ex) {

            $data = [

                'status' => "fail",

                'response' => 201,

                'data' => "token not valid",

            ];

            return $this->response->setJSON($data);

        }

    }



    public function changePassengerinfo()

    {



        $key = getenv('TOKEN_SECRET');

        $token = $this->tokenJwt->tokencheck();





        try {

            $decoded = JWT::decode($token, $key, array("HS256"));





            $userdetailId = $this->usercheck($decoded->slug);







            $validdata = array(

                "id" => $userdetailId->id,

                "first_name" => $this->request->getVar('first_name'),

                "last_name" => $this->request->getVar('last_name'),

                "id_type" => $this->request->getVar('id_type'),

                "id_number" => $this->request->getVar('id_number'),

                "country_id" => $this->request->getVar('country_id'),

            );





            if ($this->validation->run($validdata, 'userDetail')) {









                $inputdata = array(

                    "id" => $userdetailId->id,

                    "first_name" => $this->request->getVar('first_name'),

                    "last_name" => $this->request->getVar('last_name'),

                    "id_type" => $this->request->getVar('id_type'),

                    "country_id" => $this->request->getVar('country_id'),

                    "id_number" => $this->request->getVar('id_number'),

                    "address" => $this->request->getVar('address'),

                    "city" => $this->request->getVar('city'),

                    "zip_code" => $this->request->getVar('zip_code'),



                );









                $success = $this->userDetailModel->save($inputdata);



                $data = [

                    'status' => "success",

                    'response' => 200,

                    'data' => $success,

                ];



                return $this->response->setJSON($data);

            } else {

                $data = [

                    'status' => "fail",

                    'response' => 201,

                    'message' => "data validation error",

                    'data' => $this->validation->listErrors(),

                ];

                return $this->response->setJSON($data);

            }

        } catch (Exception $ex) {

            $data = [

                'status' => "fail",

                'response' => 201,

                'data' => "token not valid",

                'error' => $ex,

            ];

            return $this->response->setJSON($data);

        }

    }



    public function changePassword()

    {

        $password = $this->request->getVar('password');

        $repassword = $this->request->getVar('repassword');

        $oldpassword = $this->request->getVar('oldpassword');



        if ($password == $repassword) {



            $key = getenv('TOKEN_SECRET');

            $token = $this->tokenJwt->tokencheck();



            try {

                $decoded = JWT::decode($token, $key, array("HS256"));







                $userdetail = $this->userModel->where('slug', $decoded->slug)->first();



                $pass = $userdetail->password;

                $verify_pass = password_verify($oldpassword, $pass);



                if ($verify_pass) {

                    $newpassword = password_hash($password, PASSWORD_DEFAULT);

                    $passupdate = array(

                        "id" => $userdetail->id,

                        "password" => $newpassword,



                    );



                    $success = $this->userModel->save($passupdate);



                    $data = [

                        'status' => "success",

                        'response' => 200,

                        'data' => $success,

                    ];



                    return $this->response->setJSON($data);

                } else {

                    $data = [

                        'status' => "fail",

                        'response' => 201,

                        'data' => "old-password dosen't match",

                    ];



                    return $this->response->setJSON($data);

                }

            } catch (Exception $ex) {

                $data = [

                    'status' => "fail",

                    'response' => 201,

                    'data' => "token not valid",

                ];

                return $this->response->setJSON($data);

            }

        } else {

            $data = [

                'status' => "fail",

                'response' => 201,

                'data' => "password dosen't match",

            ];

            return $this->response->setJSON($data);

        }

    }





    public function usercheck($slag)

    {



        $userdetail = $this->userModel->where('slug', $slag)->first();

        $userdetailid = $this->userDetailModel->where('user_id', $userdetail->id)->first();

        return $userdetailid;

    }



    public function imgaeCheck($image, $path)

    {

        $newName = $image->getRandomName();

        $path = $path;

        $image->move($path, $newName);

        return $path . '/' . $newName;

    }





    public function regUserold()



    {

        $login_email = $this->request->getVar('login_email');

        $login_mobile = $this->request->getVar('login_mobile');







        $inputPass = $this->request->getVar('password');

        $password = password_hash($inputPass, PASSWORD_DEFAULT);



        $bytes = random_bytes(5);

        $slug = bin2hex($bytes);

        $role_id = 3;

        $status = 1;



        $userData = array(

            "login_email" => $login_email,

            "login_mobile" => $login_mobile,

            "password" => $password,

            "slug" => $slug,

            "role_id" => $role_id,

            "status" => $status,

        );



        $validuserData = array(

            "login_email" => $login_email,

            "login_mobile" => $login_mobile,

            "password" => $this->request->getVar('password'),

            "repassword" => $this->request->getVar('repassword'),

            "slug" => $slug,

            "role_id" => $role_id,

            "status" => $status,

        );





        if ($this->validation->run($validuserData, 'reguser')) {

            $this->db->transStart();



            $userid = $this->userModel->insert($userData);



            $validdata = array(

                "user_id" => $userid,

                "first_name" => $this->request->getVar('first_name'),

                "last_name" => $this->request->getVar('last_name'),

                "id_type" => $this->request->getVar('id_type') ?: null,

                "id_number" => $this->request->getVar('id_number') ?: null,

                "country_id" => $this->request->getVar('country_id') ?: null,

            );

            

            if ($this->validation->run($validdata, 'userDetail')) {

                $data = array(

                    "user_id" => $userid,

                    "first_name" => $this->request->getVar('first_name'),

                    "last_name" => $this->request->getVar('last_name'),

                    "id_type" => $this->request->getVar('id_type') ?: null,

                    "id_number" => $this->request->getVar('id_number') ?: null,

                    "address" => $this->request->getVar('address'),

                    "city" => $this->request->getVar('city'),

                    "zip_code" => $this->request->getVar('zip_code'),



                );



                $this->userDetailModel->insert($data);



                $this->db->transComplete();



                $data = [

                    'status' => "success",

                    'response' => 200,

                    'data' => "Registration Success",

                ];

                return $this->response->setJSON($data);

            } else {

                $data = [

                    'status' => "fail",

                    'response' => 404,

                    'error' => $this->validation->getErrors(),   //$validation->listErrors()

                    'data' => "Registration fail",

                ];

                return $this->response->setJSON($data);

            }

        } else {



            $data = [

                'status' => "fail",

                'response' => 404,

                'error' => $this->validation->getErrors(),   //$validation->listErrors()

                'data' => "Registration fail",

            ];

            return $this->response->setJSON($data);

        }

    }
    
        public function regUser()



    {

        $login_email = $this->request->getVar('login_email');

        $login_mobile = $this->request->getVar('login_mobile');

        // Check if email exists for non-deleted users only
        $existingEmail = $this->userModel->where('login_email', $login_email)->where('deleted_at', null)->first();
        if (!empty($existingEmail)) {
            return $this->response->setJSON([
                'status' => "fail",
                'response' => 400,
                'message' => "Email is already taken",
                'error' => ['login_email' => 'Email is already taken']
            ]);
        }

        // Check if mobile exists for non-deleted users only
        $existingMobile = $this->userModel->where('login_mobile', $login_mobile)->where('deleted_at', null)->first();
        if (!empty($existingMobile)) {
            return $this->response->setJSON([
                'status' => "fail",
                'response' => 400,
                'message' => "Mobile number is already taken",
                'error' => ['login_mobile' => 'Mobile number is already taken']
            ]);
        }

        // Check if email or mobile exists in deleted users (for reactivation)
        $deletedUserByEmail = $this->userModel->withDeleted()->where('login_email', $login_email)->first();
        $deletedUserByMobile = $this->userModel->withDeleted()->where('login_mobile', $login_mobile)->first();
        
        // Filter to only consider deleted users (deleted_at is not null)
        $deletedUser = null;
        if (!empty($deletedUserByEmail) && !empty($deletedUserByEmail->deleted_at)) {
            $deletedUser = $deletedUserByEmail;
            // Verify mobile also matches or is not taken by another deleted user
            if (!empty($deletedUserByMobile) && !empty($deletedUserByMobile->deleted_at) && $deletedUserByMobile->id != $deletedUser->id) {
                return $this->response->setJSON([
                    'status' => "fail",
                    'response' => 400,
                    'message' => "Email and mobile belong to different accounts",
                    'error' => ['login_email' => 'Email and mobile do not match']
                ]);
            }
        } elseif (!empty($deletedUserByMobile) && !empty($deletedUserByMobile->deleted_at)) {
            $deletedUser = $deletedUserByMobile;
        }







        $inputPass = $this->request->getVar('password');

        $password = password_hash($inputPass, PASSWORD_DEFAULT);

        $bytes = random_bytes(5);

        $slug = bin2hex($bytes);

        $role_id = 3;

        $status = 1;

        // If deleted user found, reactivate them instead of creating new account
        if (!empty($deletedUser)) {
            // Validate password inputs
            $passwordInput = $this->request->getVar('password');
            $repasswordInput = $this->request->getVar('repassword');
            
            if (empty($passwordInput) || empty($repasswordInput)) {
                return $this->response->setJSON([
                    'status' => "fail",
                    'response' => 400,
                    'message' => "Password and confirm password are required",
                    'error' => ['password' => 'Password fields are required']
                ]);
            }

            if ($passwordInput !== $repasswordInput) {
                return $this->response->setJSON([
                    'status' => "fail",
                    'response' => 400,
                    'message' => "Passwords do not match",
                    'error' => ['repassword' => 'Passwords do not match']
                ]);
            }

            // Validate user details input
            $validdata = array(
                "user_id" => $deletedUser->id,
                "first_name" => $this->request->getVar('first_name'),
                "last_name" => $this->request->getVar('last_name'),
                "id_type" => $this->request->getVar('id_type') ?: null,
                "id_number" => $this->request->getVar('id_number') ?: null,
                "country_id" => $this->request->getVar('country_id') ?: null,
            );

            if (!$this->validation->run($validdata, 'userDetail')) {
                return $this->response->setJSON([
                    'status' => "fail",
                    'response' => 404,
                    'error' => $this->validation->getErrors(),
                    'data' => "Account reactivation failed",
                ]);
            }

            try {
                $this->db->transStart();

                $userId = $deletedUser->id;

                // Restore user: remove deleted_at, update password, set status to active
                $this->userModel->update($userId, [
                    'deleted_at' => null,
                    'password' => $password,
                    'status' => 1
                ]);

                // Check if user_details exists for this user (including deleted)
                $userDetail = $this->userDetailModel->withDeleted()->where('user_id', $userId)->first();

                $userDetailData = [
                    'first_name' => $this->request->getVar('first_name'),
                    'last_name' => $this->request->getVar('last_name'),
                    'id_type' => $this->request->getVar('id_type') ?: null,
                    'id_number' => $this->request->getVar('id_number') ?: null,
                    'address' => $this->request->getVar('address'),
                    'city' => $this->request->getVar('city'),
                    'zip_code' => $this->request->getVar('zip_code'),
                    'country_id' => $this->request->getVar('country_id') ?: null,
                ];

                if (!empty($userDetail)) {
                    // Restore and update user_details
                    $userDetailData['deleted_at'] = null;
                    $this->userDetailModel->update($userDetail->id, $userDetailData);
                } else {
                    // Create new user_details if it doesn't exist
                    $userDetailData['user_id'] = $userId;
                    $this->userDetailModel->insert($userDetailData);
                }

                $this->db->transComplete();

                return $this->response->setJSON([
                    'status' => "success",
                    'response' => 200,
                    'data' => "Account reactivated successfully",
                ]);

            } catch (\Throwable $e) {
                if ($this->db->transStatus() === false) {
                    $this->db->transRollback();
                }
                return $this->response->setJSON([
                    'status' => "fail",
                    'response' => 500,
                    'message' => "Error reactivating account: " . $e->getMessage(),
                ]);
            }
        }

        // Normal registration flow for new users
        $userData = array(

            "login_email" => $login_email,

            "login_mobile" => $login_mobile,

            "password" => $password,

            "slug" => $slug,

            "role_id" => $role_id,

            "status" => $status,

        );



        $validuserData = array(

            "login_email" => $login_email,

            "login_mobile" => $login_mobile,

            "password" => $this->request->getVar('password'),

            "repassword" => $this->request->getVar('repassword'),

            "slug" => $slug,

            "role_id" => $role_id,

            "status" => $status,

        );





        if ($this->validation->run($validuserData, 'reguser')) {

            $this->db->transStart();



            $userid = $this->userModel->insert($userData);



            $validdata = array(

                "user_id" => $userid,

                "first_name" => $this->request->getVar('first_name'),

                "last_name" => $this->request->getVar('last_name'),

                "id_type" => $this->request->getVar('id_type') ?: null,

                "id_number" => $this->request->getVar('id_number') ?: null,

                "country_id" => $this->request->getVar('country_id') ?: null,

            );

            

            if ($this->validation->run($validdata, 'userDetail')) {

                $data = array(

                    "user_id" => $userid,

                    "first_name" => $this->request->getVar('first_name'),

                    "last_name" => $this->request->getVar('last_name'),

                    "id_type" => $this->request->getVar('id_type') ?: null,

                    "id_number" => $this->request->getVar('id_number') ?: null,

                    "address" => $this->request->getVar('address'),

                    "city" => $this->request->getVar('city'),

                    "zip_code" => $this->request->getVar('zip_code'),



                );



                $this->userDetailModel->insert($data);



                $this->db->transComplete();



                $data = [

                    'status' => "success",

                    'response' => 200,

                    'data' => "Registration Success",

                ];

                return $this->response->setJSON($data);

            } else {

                $data = [

                    'status' => "fail",

                    'response' => 404,

                    'error' => $this->validation->getErrors(),   //$validation->listErrors()

                    'data' => "Registration fail",

                ];

                return $this->response->setJSON($data);

            }

        } else {



            $data = [

                'status' => "fail",

                'response' => 404,

                'error' => $this->validation->getErrors(),   //$validation->listErrors()

                'data' => "Registration fail",

            ];

            return $this->response->setJSON($data);

        }

    }



    public function loginsocial()

    {





        $appid = $this->request->getVar('appid');

        $first_name = $this->request->getVar('first_name');

        $last_name = $this->request->getVar('last_name');

        $email = $this->request->getVar('email');



        $getAppid = $this->socialsigninModel->where('appid', $appid)->where('email', $email)->first();



        if (!empty($getAppid)) {







            $userdetail = $this->userModel->join('user_details', 'user_details.user_id = users.id', 'left')->where('role_id', 3)->where('status', 1)

                ->where('login_email', $email)

                ->where('login_mobile', $appid)

                ->first();





            if ($userdetail) {



                $token = $this->tokenJwt->generateToken($userdetail->slug);



                $data = [

                    'status' => "success",

                    'response' => 200,

                    'data' => $token,

                ];



                return $this->response->setJSON($data);

            } else {

                $data = [

                    'message' => "User  Not Found",

                    'status' => "fail",

                    'response' => 204,



                ];

                return $this->response->setJSON($data);

            }

        } else {







            $socialdatavalidation = [

                'appid' => $appid,

                'email' => $email,

            ];



            $socialdata = [

                'appid' => $appid,

                'email' => $email,

                'other' => $this->request->getVar('other'),

            ];



            if ($this->validation->run($socialdatavalidation, 'socialsingup')) {







                $this->socialsigninModel->insert($socialdata);





                $inputPass = $appid;

                $password = password_hash($inputPass, PASSWORD_DEFAULT);



                $bytes = random_bytes(5);

                $slug = bin2hex($bytes);

                $role_id = 3;

                $status = 1;



                $userData = array(

                    "login_email" => $email,

                    "login_mobile" => $appid,

                    "password" => $password,

                    "slug" => $slug,

                    "role_id" => $role_id,

                    "status" => $status,

                );



                if ($this->validation->run($userData, 'user')) {



                    $userid = $this->userModel->insert($userData);



                    $validdata = array(

                        "user_id" => $userid,

                        "first_name" => $first_name,

                        "last_name" => $last_name,

                        "id_type" => "passport",

                        "id_number" => $appid ?: null,

                        "country_id" => 14,

                    );



                    if ($this->validation->run($validdata, 'userDetail')) {

                        $data = array(

                            "user_id" => $userid,

                            "first_name" => $first_name,

                            "last_name" => $last_name,

                            "id_type" => "passport",

                            "id_number" => $appid ?: null,

                            "country_id" => 14,



                        );



                        $this->userDetailModel->insert($data);





                        $userdetail = $this->userModel->join('user_details', 'user_details.user_id = users.id', 'left')->where('role_id', 3)->where('status', 1)

                            ->where('login_email', $email)

                            ->where('login_mobile', $appid)

                            ->first();





                        if ($userdetail) {



                            $token = $this->tokenJwt->generateToken($userdetail->slug);



                            $data = [

                                'status' => "success",

                                'response' => 200,

                                'data' => $token,

                            ];



                            return $this->response->setJSON($data);

                        } else {

                            $data = [

                                'message' => "User  Not Found",

                                'status' => "fail",

                                'response' => 204,



                            ];

                            return $this->response->setJSON($data);

                        }

                    }

                } else {

                    $data = [

                        'status' => "fail",

                        'response' => 404,

                        'error' => $this->validation->getErrors(),   //$validation->listErrors()

                        'data' => "Registration fail",

                    ];

                    return $this->response->setJSON($data);

                }

            } else {



                $data = [

                    'status' => "fail",

                    'response' => 404,

                    'error' => $this->validation->getErrors(),   //$validation->listErrors()

                    'data' => "Registration fail",

                ];

                return $this->response->setJSON($data);

            }

        }

    }





    public function checkEmail()

    {

        $email = $this->request->getVar('login_email');



        // $emailDetail = $this->userModel->where('login_email', $email)->first();
        
        $emailDetail = $this->userModel->where('login_email', $email)->where('deleted_at', null)->first();



        if (empty($emailDetail)) {

            $data = [

                'message' => "No Email address found",

                'status' => "fail",

                'response' => 204,



            ];

            return $this->response->setJSON($data);

        } else {

            $data = [

                'message' => "Email address found",

                'status' => "success",

                'response' => 204,



            ];

            return $this->response->setJSON($data);

        }

    }







    public function checkMobile()

    {

        $mobile = $this->request->getVar('login_mobile');



        // $emailDetail = $this->userModel->where('login_mobile', $mobile)->first();
        
        $emailDetail = $this->userModel->where('login_mobile', $mobile)->where('deleted_at', null)->first();



        if (empty($emailDetail)) {

            $data = [

                'message' => "No Mobile Number found",

                'status' => "fail",

                'response' => 204,



            ];

            return $this->response->setJSON($data);

        } else {

            $data = [

                'message' => "Mobile Number found",

                'status' => "success",

                'response' => 204,



            ];

            return $this->response->setJSON($data);

        }

    }





    public function checkIdNumber()

    {

        $idnumber = $this->request->getVar('id_number');



        $idDetail = $this->userDetailModel->where('id_number', $idnumber)->first();



        if (empty($idDetail)) {

            $data = [

                'message' => "No Id Number found",

                'status' => "fail",

                'response' => 204,



            ];

            return $this->response->setJSON($data);

        } else {

            $data = [

                'message' => "ID Number found",

                'status' => "success",

                'response' => 204,



            ];

            return $this->response->setJSON($data);

        }

    }

    public function deviceRegister()

    {

        $rules = [

            'user_id'      => 'required|integer',

            'device_token' => 'required|string|max_length[255]',

            'device_type'  => 'permit_empty|in_list[android,ios,web]',

        ];



        if (!$this->validate($rules)) {

            return $this->response->setJSON([

                'status'  => false,

                'message' => $this->validator->getErrors()

            ])->setStatusCode(422);

        }



        $userId = $this->request->getVar('user_id');

        $token  = $this->request->getVar('device_token');

        $type   = $this->request->getVar('device_type') ?? 'android';



        // $deviceModel = new UserDeviceModel();



        // check if token already exists (same user or not)

        $existing = $this->userdeviceModel->where('device_token', $token)->first();



        if ($existing) {

            // update user_id if needed

            $this->userdeviceModel->update($existing->id, [

                'user_id' => $userId,

                'device_type' => $type,

                'status' => 1

            ]);

        } else {

            $this->userdeviceModel->insert([

                'user_id' => $userId,

                'device_token' => $token,

                'device_type' => $type,

                'status' => 1

            ]);

        }



        return $this->response->setJSON([

            'status'  => true,

            'message' => 'Device token registered successfully.'

        ]);

    }



    public function sendNotification()

    {

        $rules = [

            'user_id' => 'required|integer',

            'title'   => 'required|string|max_length[255]',

            'body'    => 'required|string|max_length[500]',

        ];



        if (!$this->validate($rules)) {

            return $this->response->setJSON([

                'status'  => false,

                'message' => $this->validator->getErrors()

            ])->setStatusCode(422);

        }



        $userId = $this->request->getVar('user_id');

        $title  = $this->request->getVar('title');

        $body   = $this->request->getVar('body');



        // Fetch device tokens from DB

        $devices = $this->userdeviceModel->where('user_id', $userId)

                                        ->where('status', 1)

                                        ->findAll();



        if (empty($devices)) {

            return $this->response->setJSON([

                'status'  => false,

                'message' => 'No active device tokens found for user.'

            ]);

        }



        // $firebase = new \App\Libraries\FirebaseNotification();



        foreach ($devices as $device) {

            try {

                $this->FirebaseNotification->sendToToken($device->device_token, $title, $body);

            } catch (\Throwable $e) {

                log_message('error', 'FCM Error: '.$e->getMessage());

            }

        }



        return $this->response->setJSON([

            'status'  => true,

            'message' => 'Notification sent successfully.'

        ]);

    }
    public function updateLanguage()
    {
    
        $userId = $this->request->getVar('user_id');
        $language = $this->request->getVar('language');
    
        // Basic validation
        if (empty($userId) || empty($language)) {
            return $this->response->setStatusCode(400)->setJSON([
                'status'   => 'fail',
                'response' => 400,
                'message'  => 'Missing required fields: user_id or language.'
            ]);
        }
    
        // Check if user exists
        $user = $this->userModel->find($userId);
        if (!$user) {
            return $this->response->setStatusCode(404)->setJSON([
                'status'   => 'fail',
                'response' => 404,
                'message'  => 'User not found.'
            ]);
        }
    
        // Prepare update data
        $updateData = ['language' => $language];
    
        // Extra safety: check that allowedFields includes 'language'
        if (!in_array('language', $this->userModel->allowedFields ?? [])) {
            return $this->response->setStatusCode(500)->setJSON([
                'status'   => 'fail',
                'response' => 500,
                'message'  => "The 'language' field is not allowed in UserModel."
            ]);
        }
    
        // Perform update
        if ($this->userModel->update($userId, $updateData)) {
            return $this->response->setStatusCode(200)->setJSON([
                'status'   => 'success',
                'response' => 200,
                'message'  => 'Language updated successfully.'
            ]);
        } else {
            return $this->response->setStatusCode(500)->setJSON([
                'status'   => 'fail',
                'response' => 500,
                'message'  => 'Failed to update language.'
            ]);
        }
    }
    
    public function deleteUser($user_id)
    {
        try {
            // Get deletion reason from request (supports both JSON and form data)
            $reason_id = $this->request->getVar('reason_id');
            $custom_reason = $this->request->getVar('custom_reason');
            
            // Also try to get from JSON if sent as JSON
            if (empty($reason_id)) {
                $jsonData = $this->request->getJSON(true);
                if ($jsonData) {
                    $reason_id = $jsonData['reason_id'] ?? null;
                    $custom_reason = $jsonData['custom_reason'] ?? null;
                }
            }

            // Validate reason_id is provided
            if (empty($reason_id)) {
                return $this->response->setJSON([
                    'status' => "fail",
                    'response' => 400,
                    'message' => "Deletion reason (reason_id) is required"
                ]);
            }

            // Validate reason_id exists
            $reasonExists = $this->userDeletionReasonModel->where('id', $reason_id)->where('status', 1)->first();
            if (empty($reasonExists)) {
                return $this->response->setJSON([
                    'status' => "fail",
                    'response' => 400,
                    'message' => "Invalid deletion reason"
                ]);
            }

            // If reason is "Other", custom_reason is required
            if ($reason_id == 6 && empty($custom_reason)) {
                return $this->response->setJSON([
                    'status' => "fail",
                    'response' => 400,
                    'message' => "Custom reason is required when selecting 'Other'"
                ]);
            }

            // Find user detail by user_id (foreign key column in user_details table)
            $passangerInfo = $this->userDetailModel->where('user_id', $user_id)->first();
            
            if (empty($passangerInfo)) {
                $data = [
                    'status' => "fail",
                    'response' => 404,
                    'message' => "User not found",
                ];
                return $this->response->setJSON($data);
            }

            $passangerUserId = $passangerInfo->user_id;
            $passangerDetailId = $passangerInfo->id;
            $passangerLastName = $passangerInfo->last_name;

            // Start database transaction
            $this->db->transStart();

            // Set status to 0 in users table
            $this->userModel->update($passangerUserId, ['status' => 0]);

            // Soft delete user_detail and user records
            $this->userDetailModel->delete($passangerDetailId);
            $this->userModel->delete($passangerUserId);

            // Store deletion record with reason
            $deletionData = [
                'user_id' => $passangerUserId,
                'reason_id' => $reason_id,
                'custom_reason' => !empty($custom_reason) ? $custom_reason : null,
                'deleted_at' => date('Y-m-d H:i:s')
            ];
            $this->userDeletionModel->insert($deletionData);

            // Complete transaction
            $this->db->transComplete();

            if ($this->db->transStatus() === false) {
                $data = [
                    'status' => "fail",
                    'response' => 500,
                    'message' => "Failed to delete user",
                ];
                return $this->response->setJSON($data);
            }

            $data = [
                'status' => "success",
                'response' => 200,
                'message' => "User deleted successfully",
                'data' => [
                    'user_id' => $user_id,
                    'name' => $passangerLastName
                ]
            ];

            return $this->response->setJSON($data);

        } catch (\Throwable $e) {
            $data = [
                'status' => "fail",
                'response' => 500,
                'message' => "Error: " . $e->getMessage(),
            ];
            return $this->response->setJSON($data);
        }
    }

    /**
     * Guidance for app/website UI (amounts are already computed in cancellation_preview).
     *
     * @return array<string, string>
     */
    protected function getCancellationPolicyHelp(): array
    {
        return [
            'summary' => 'Call GET tickets/cancel-preview when the user taps Cancel. Show summary.cancel_fee and summary.refund_amount. On confirm, POST tickets/cancel; final amounts are in the POST response (recalculated at confirm time).',
            'tier_selection' => 'Server picks the global tier where tier.hours >= remaining_hours (smallest such tier). If the trip is farther out than every tier, the widest tier (max hours) is used.',
            'charge_percent' => 'Percentage of ticket_amount (paidamount). cancel_fee = round(ticket_amount * charge_percent / 100). refund_amount = ticket_amount - cancel_fee.',
            'user_notice' => TicketCancellationSupport::getCancellationIntegrationGuide()['user_notice'],
        ];
    }

    /**
     * booking_id / ticket_id / detail from query, form POST, or JSON body (website axios/fetch).
     *
     * @return array{booking_id: string|null, ticket_id: string|null, detail: string|null}
     */
    protected function getCancelRequestInput(): array
    {
        $json = $this->request->getJSON(true);
        if (! is_array($json)) {
            $json = [];
        }

        $bookingId = $this->request->getGet('booking_id')
            ?? $this->request->getPost('booking_id')
            ?? $this->request->getVar('booking_id')
            ?? ($json['booking_id'] ?? null)
            ?? ($json['bookingId'] ?? null);

        $ticketId = $this->request->getGet('ticket_id')
            ?? $this->request->getPost('ticket_id')
            ?? $this->request->getVar('ticket_id')
            ?? ($json['ticket_id'] ?? null)
            ?? ($json['ticketId'] ?? null);

        $detail = $this->request->getPost('detail')
            ?? $this->request->getVar('detail')
            ?? ($json['detail'] ?? null);

        return [
            'booking_id' => $bookingId !== null && $bookingId !== '' ? trim((string) $bookingId) : null,
            'ticket_id' => $ticketId !== null && $ticketId !== '' ? trim((string) $ticketId) : null,
            'detail' => $detail !== null ? trim((string) $detail) : null,
        ];
    }

    /**
     * @return list<object>
     */
    protected function findActivePassengerTickets(string $bookingId, int $userId, int $ticketId = 0): array
    {
        $ticketQuery = $this->ticketModel
            ->where('booking_id', $bookingId)
            ->where('passanger_id', $userId)
            ->groupStart()
                ->where('cancel_status', 0)
                ->orWhere('cancel_status', '0')
            ->groupEnd();

        if ($ticketId > 0) {
            $ticketQuery->where('id', $ticketId);
        }

        return $ticketQuery->findAll();
    }

    /**
     * Idempotent success when cancel is requested again after tickets are already cancelled.
     *
     * @return array<string, mixed>|null
     */
    protected function buildAlreadyCancelledSuccessPayload(string $bookingId, int $userId, int $ticketId = 0): ?array
    {
        $ticketQuery = $this->ticketModel
            ->where('booking_id', $bookingId)
            ->where('passanger_id', $userId)
            ->groupStart()
                ->where('cancel_status', 1)
                ->orWhere('cancel_status', '1')
            ->groupEnd();

        if ($ticketId > 0) {
            $ticketQuery->where('id', $ticketId);
        }

        $tickets = $ticketQuery->findAll();
        if (empty($tickets)) {
            return null;
        }

        $ticketIds = array_map(static fn ($t) => (int) $t->id, $tickets);
        $cancelRows = $this->cancelModel
            ->where('booking_id', $bookingId)
            ->where('type', 'ticket')
            ->whereIn('track_table_id', $ticketIds)
            ->orderBy('id', 'DESC')
            ->findAll();

        $cancelByTicketId = [];
        foreach ($cancelRows as $cancelRow) {
            $tid = (int) ($cancelRow->track_table_id ?? 0);
            if ($tid > 0 && ! isset($cancelByTicketId[$tid])) {
                $cancelByTicketId[$tid] = $cancelRow;
            }
        }

        $totalTicketAmount = 0.0;
        $totalRefundAmount = 0.0;
        $totalCancelFee = 0.0;
        $cancelledTickets = [];
        $aggregateRefundStatus = 'done';

        foreach ($tickets as $ticket) {
            $tid = (int) $ticket->id;
            $cancelRow = $cancelByTicketId[$tid] ?? null;

            $ticketAmount = $cancelRow !== null
                ? (float) ($cancelRow->ticket_amount ?? 0)
                : (float) ($ticket->paidamount ?? 0);
            $cancelFee = $cancelRow !== null ? (float) ($cancelRow->cancel_fee ?? 0) : 0.0;
            $refundAmount = $cancelRow !== null ? (float) ($cancelRow->refund_amount ?? 0) : 0.0;

            if ($cancelRow !== null && ($cancelRow->status ?? '') === 'pending') {
                $aggregateRefundStatus = 'pending';
            }

            $totalTicketAmount += $ticketAmount;
            $totalCancelFee += $cancelFee;
            $totalRefundAmount += $refundAmount;

            $cancelledTickets[] = [
                'ticket_id' => $tid,
                'ticket_amount' => $ticketAmount,
                'cancel_fee' => $cancelFee,
                'refund_amount' => $refundAmount,
            ];
        }

        return [
            'booking_id' => $bookingId,
            'already_cancelled' => true,
            'computed_at' => date('c'),
            'refund_status' => $aggregateRefundStatus,
            'refund_amount' => round($totalRefundAmount, 2),
            'cancel_fee' => round($totalCancelFee, 2),
            'ticket_amount' => round($totalTicketAmount, 2),
            'tickets' => $cancelledTickets,
        ];
    }

    /**
     * Preview cancellation fees before approval (app & website confirm screen).
     * GET ?booking_id=TBxxx&ticket_id=123 (ticket_id optional)
     */
    public function cancelTicketPreview()
    {
        $key = getenv('TOKEN_SECRET');
        $token = $this->tokenJwt->tokencheck();

        try {
            $decoded = JWT::decode($token, $key, ['HS256']);
            $userRow = $this->userModel->where('slug', $decoded->slug)->first();
            if (empty($userRow)) {
                return $this->response->setJSON([
                    'status' => 'fail',
                    'response' => 404,
                    'message' => 'User not found',
                ]);
            }

            $cancelInput = $this->getCancelRequestInput();
            $booking_id = $cancelInput['booking_id'];
            if (empty($booking_id)) {
                return $this->response->setJSON([
                    'status' => 'fail',
                    'response' => 400,
                    'message' => 'booking_id is required',
                ]);
            }

            $ticketIdInt = is_numeric($cancelInput['ticket_id'] ?? '') ? (int) $cancelInput['ticket_id'] : 0;

            $tickets = $this->findActivePassengerTickets($booking_id, (int) $userRow->id, $ticketIdInt);

            if (empty($tickets)) {
                $alreadyCancelled = $this->buildAlreadyCancelledSuccessPayload(
                    $booking_id,
                    (int) $userRow->id,
                    $ticketIdInt
                );
                if ($alreadyCancelled !== null) {
                    return $this->response->setJSON([
                        'status' => 'success',
                        'response' => 200,
                        'message' => 'Ticket is already cancelled',
                        'global_cancellation_policy' => $this->getGlobalCancellationPolicyForApi(),
                        'cancellation_policy_help' => $this->getCancellationPolicyHelp(),
                        'cancellation_integration' => TicketCancellationSupport::getCancellationIntegrationGuide(),
                        'data' => [
                            'booking_id' => $booking_id,
                            'can_cancel' => false,
                            'already_cancelled' => true,
                            'computed_at' => $alreadyCancelled['computed_at'],
                            'tickets' => [],
                            'summary' => [
                                'ticket_amount' => $alreadyCancelled['ticket_amount'],
                                'cancel_fee' => $alreadyCancelled['cancel_fee'],
                                'refund_amount' => $alreadyCancelled['refund_amount'],
                            ],
                        ],
                    ]);
                }

                return $this->response->setJSON([
                    'status' => 'fail',
                    'response' => 404,
                    'message' => 'Ticket not found',
                    'global_cancellation_policy' => $this->getGlobalCancellationPolicyForApi(),
                    'cancellation_policy_help' => $this->getCancellationPolicyHelp(),
                    'cancellation_integration' => TicketCancellationSupport::getCancellationIntegrationGuide(),
                ]);
            }

            $previews = [];
            $sumTicket = 0.0;
            $sumFee = 0.0;
            $sumRefund = 0.0;
            $canCancelAll = true;

            foreach ($tickets as $ticket) {
                $preview = TicketCancellationSupport::buildPassengerCancellationPreview((int) $ticket->id);
                if ($preview === null) {
                    continue;
                }
                $previews[] = $preview;
                if (empty($preview['can_cancel'])) {
                    $canCancelAll = false;
                    continue;
                }
                $sumTicket += (float) ($preview['ticket_amount'] ?? 0);
                $sumFee += (float) ($preview['cancel_fee'] ?? 0);
                $sumRefund += (float) ($preview['refund_amount'] ?? 0);
            }

            return $this->response->setJSON([
                'status' => 'success',
                'response' => 200,
                'message' => 'Cancellation fee preview',
                'global_cancellation_policy' => $this->getGlobalCancellationPolicyForApi(),
                'cancellation_policy_help' => $this->getCancellationPolicyHelp(),
                'cancellation_integration' => TicketCancellationSupport::getCancellationIntegrationGuide(),
                'data' => [
                    'booking_id' => $booking_id,
                    'can_cancel' => $canCancelAll,
                    'computed_at' => date('c'),
                    'tickets' => $previews,
                    'summary' => [
                        'ticket_amount' => round($sumTicket, 2),
                        'cancel_fee' => round($sumFee, 2),
                        'refund_amount' => round($sumRefund, 2),
                    ],
                ],
            ]);
        } catch (\Throwable $e) {
            return $this->response->setJSON([
                'status' => 'fail',
                'response' => 401,
                'message' => 'Unauthorized or invalid request',
            ]);
        }
    }

    public function cancelTicket()
    {
        $key = getenv('TOKEN_SECRET');
        $token = $this->tokenJwt->tokencheck();

        try {
            $decoded = JWT::decode($token, $key, ['HS256']);
            $userRow = $this->userModel->where('slug', $decoded->slug)->first();
            if (empty($userRow)) {
                return $this->response->setJSON([
                    'status' => 'fail',
                    'response' => 404,
                    'message' => 'User not found',
                ]);
            }

            $cancelInput = $this->getCancelRequestInput();
            $booking_id = $cancelInput['booking_id'];
            if (empty($booking_id)) {
                return $this->response->setJSON([
                    'status' => 'fail',
                    'response' => 400,
                    'message' => 'booking_id is required',
                ]);
            }

            // Optional: if the client sends ticket_id, cancel only that ticket.
            $ticketIdInt = is_numeric($cancelInput['ticket_id'] ?? '') ? (int) $cancelInput['ticket_id'] : 0;

            $tickets = $this->findActivePassengerTickets($booking_id, (int) $userRow->id, $ticketIdInt);

            if (empty($tickets)) {
                $alreadyCancelled = $this->buildAlreadyCancelledSuccessPayload(
                    $booking_id,
                    (int) $userRow->id,
                    $ticketIdInt
                );
                if ($alreadyCancelled !== null) {
                    return $this->response->setJSON([
                        'status' => 'success',
                        'response' => 200,
                        'message' => 'Ticket cancelled successfully',
                        'data' => $alreadyCancelled,
                    ]);
                }

                return $this->response->setJSON([
                    'status' => 'fail',
                    'response' => 404,
                    'message' => 'Ticket not found',
                ]);
            }

            // Validate all tickets first (so we don't partially cancel).
            $computed = [];
            foreach ($tickets as $ticket) {
                if (($ticket->bookby_user_type ?? '') !== 'passanger') {
                    return $this->response->setJSON([
                        'status' => 'fail',
                        'response' => 403,
                        'message' => 'Only passenger online bookings can be cancelled via this API',
                    ]);
                }

                $track = TicketCancellationSupport::loadTrackRowForCancellation((int) $ticket->id);
                if ($track === null) {
                    return $this->response->setJSON([
                        'status' => 'fail',
                        'response' => 500,
                        'message' => 'Unable to load trip details',
                    ]);
                }

                $amounts = TicketCancellationSupport::computeCancellationAmounts($track);
                if (! empty($amounts['journey_passed'])) {
                    return $this->response->setJSON([
                        'status' => 'fail',
                        'response' => 400,
                        'message' => 'Journey has already started or passed',
                    ]);
                }

                $computed[] = [$ticket, $amounts];
            }

            $detail = $cancelInput['detail'];
            if ($detail === null || $detail === '') {
                // Fallback to any ticket's payment_detail (same as previous behavior).
                $detail = $tickets[0]->payment_detail ?? '';
            }

            $totalTicketAmount = 0;
            $totalRefundAmount = 0;
            $totalCancelFee = 0;
            $aggregateRefundStatus = 'done';
            $cancelledTickets = [];

            $this->db->transStart();
            try {
                foreach ($computed as $item) {
                    [$ticket, $amounts] = $item;
                    $refundRow = (float) ($amounts['refunded_amount'] ?? 0);
                    $rowStatus = $refundRow > 0 ? 'pending' : 'done';
                    if ($rowStatus === 'pending') {
                        $aggregateRefundStatus = 'pending';
                    }

                    $this->cancelModel->insert([
                        'booking_id' => $booking_id,
                        'cancel_fee' => $amounts['cancel_charge'],
                        'track_table_id' => $ticket->id,
                        'type' => 'ticket',
                        'detail' => $detail,
                        'pay_type_id' => $ticket->pay_method_id ?? null,
                        'cancel_by' => $userRow->id,
                        'ticket_amount' => $amounts['total_amount'],
                        'refund_amount' => $amounts['refunded_amount'],
                        'status' => $rowStatus,
                    ]);

                    $this->ticketModel->update($ticket->id, ['cancel_status' => 1]);

                    $totalTicketAmount += (float) $amounts['total_amount'];
                    $totalRefundAmount += (float) $amounts['refunded_amount'];
                    $totalCancelFee += (float) $amounts['cancel_charge'];

                    $cancelledTickets[] = [
                        'ticket_id' => (int) $ticket->id,
                        'ticket_amount' => (float) $amounts['total_amount'],
                        'cancel_fee' => (float) $amounts['cancel_charge'],
                        'refund_amount' => (float) $amounts['refunded_amount'],
                    ];
                }

                // Remove journey list entries for the whole booking once.
                $this->journeylistModel->where('booking_id', $booking_id)->delete();
                $this->db->transComplete();
            } catch (\Throwable $txe) {
                $this->db->transComplete();
                throw $txe;
            }

            if ($this->db->transStatus() === false) {
                return $this->response->setJSON([
                    'status' => 'fail',
                    'response' => 500,
                    'message' => 'Cancellation failed',
                ]);
            }

            return $this->response->setJSON([
                'status' => 'success',
                'response' => 200,
                'message' => 'Ticket cancelled successfully',
                'data' => [
                    'booking_id' => $booking_id,
                    'computed_at' => date('c'),
                    'refund_status' => $aggregateRefundStatus,
                    'refund_amount' => (float) $totalRefundAmount,
                    'cancel_fee' => (float) $totalCancelFee,
                    'ticket_amount' => (float) $totalTicketAmount,
                    'tickets' => $cancelledTickets,
                ],
            ]);
        } catch (\Throwable $e) {
            return $this->response->setJSON([
                'status' => 'fail',
                'response' => 401,
                'message' => 'Unauthorized or invalid request',
            ]);
        }
    }

    public function getDeletionReasons()
    {
        try {
            // Get all active deletion reasons
            $reasons = $this->userDeletionReasonModel
                ->where('status', 1)
                ->orderBy('sort_order', 'ASC')
                ->orderBy('id', 'ASC')
                ->findAll();

            $data = [
                'status' => "success",
                'response' => 200,
                'message' => "Deletion reasons retrieved successfully",
                'data' => $reasons
            ];

            return $this->response->setJSON($data);

        } catch (\Throwable $e) {
            $data = [
                'status' => "fail",
                'response' => 500,
                'message' => "Error: " . $e->getMessage(),
            ];
            return $this->response->setJSON($data);
        }
    }

}
