import { Response } from "express";
import { Types } from "mongoose";
import Vehicle from "../models/vehicleModel";
import { IVehicle } from "../interfaces/models/vehicleInterface";
import ResponseUtil from "../utils/Response/responseUtils";
import { STATUS_CODES } from "../constants/statusCodes";
import { VEHICLE_CONSTANTS } from "../constants/messages";
import { CustomRequest } from "../interfaces/auth";
import UserModel from "../models/UserModel";
import SmartcarAccountModel from "../models/SmartcarAccountModel";
import { getCachedSmartcarIamApplicationAccessToken } from "../services/smartcarIamApplicationToken";
import {
  syncSmartcarVehicles,
  afterSmartcarVehicleSync,
} from "../services/smartcarService";
import { vehicleArraySchema } from "../validators/vehicleValidator";

export const addVehicle = async (req: CustomRequest, res: Response) => {
  try {
    const vehicleData = await vehicleArraySchema.parseAsync(req.body);
    const userId = req.userId;

    for (const vehicle of vehicleData) {
      const existingVehicle = await Vehicle.findOne({
        userId: userId,
        name: vehicle.name,
        vehicleModel: vehicle.vehicleModel,
        isDeleted: false,
      });
      if (existingVehicle) {
        return ResponseUtil.errorResponse(
          res,
          STATUS_CODES.CONFLICT,
          `A vehicle with name ${vehicle.name} and model ${vehicle.vehicleModel} is already registered for you.`,
        );
      }
    }

    const vehiclesWithUser = vehicleData.map((vehicle) => ({
      ...vehicle,
      userId: req.userId,
    }));

    const newVehicles = await Vehicle.insertMany(vehiclesWithUser);

    await UserModel.findByIdAndUpdate(userId, { isCarsRegistered: true });

    return ResponseUtil.successResponse(
      res,
      STATUS_CODES.SUCCESS,
      { newVehicles },
      VEHICLE_CONSTANTS.VEHICLE_ADDED,
    );
  } catch (e: any) {
    ResponseUtil.handleError(res, e);
  }
};

export const getAllVehicles = async (req: CustomRequest, res: Response) => {
  try {
    const userId = req.userId;
    console.log("[Vehicles][DEBUG] getAllVehicles — userId=%s", userId);

    let vehicles = await Vehicle.find({ isDeleted: false, userId });

    if (vehicles.length === 0 && userId) {
      console.log(
        "[Vehicles][DEBUG] No vehicles in DB — checking SmartcarAccount…",
      );
      const smartcarAccount = await SmartcarAccountModel.findOne({
        userId: new Types.ObjectId(userId),
        status: true,
      });

      if (smartcarAccount?.smartcarUserId) {
        console.log(
          "[Vehicles][DEBUG] SmartcarAccount found (smartcarUserId=%s) — syncing vehicles from Smartcar…",
          smartcarAccount.smartcarUserId.slice(0, 8) + "…",
        );

        let accessToken = smartcarAccount.accessToken;
        const isIamToken = !smartcarAccount.refreshToken;
        const tokenExpired =
          smartcarAccount.tokenExpiresAt &&
          new Date(smartcarAccount.tokenExpiresAt) < new Date();

        if (isIamToken && tokenExpired) {
          console.log(
            "[Vehicles][DEBUG] IAM token expired — getting fresh token…",
          );
          try {
            const freshIam = await getCachedSmartcarIamApplicationAccessToken({
              clientId: process.env.SMARTCAR_IAM_CLIENT_ID?.trim(),
              clientSecret: process.env.SMARTCAR_CLIENT_SECRET?.trim(),
            });
            accessToken = freshIam.accessToken;
            await SmartcarAccountModel.updateOne(
              { _id: smartcarAccount._id },
              {
                $set: {
                  accessToken: freshIam.accessToken,
                  tokenExpiresAt: new Date(freshIam.expiresAtMs),
                },
              },
            );
            console.log("[Vehicles][DEBUG] Fresh IAM token obtained ✓");
          } catch (iamErr) {
            console.error("[Vehicles][DEBUG] Fresh IAM token FAILED:", iamErr);
          }
        }

        try {
          const iamScUserId = isIamToken
            ? smartcarAccount.smartcarUserId
            : undefined;
          await syncSmartcarVehicles(
            userId,
            accessToken,
            smartcarAccount.smartcarUserId,
            iamScUserId,
          );
          await afterSmartcarVehicleSync(
            userId,
            smartcarAccount.smartcarUserId,
            true,
          );
          vehicles = await Vehicle.find({ isDeleted: false, userId });
          console.log(
            "[Vehicles][DEBUG] Smartcar sync complete ✓ vehicles=%d",
            vehicles.length,
          );
        } catch (syncErr) {
          console.error("[Vehicles][DEBUG] Smartcar sync failed:", syncErr);
        }
      }
    }

    if (!vehicles || vehicles.length === 0) {
      return ResponseUtil.errorResponse(
        res,
        STATUS_CODES.SUCCESS,
        VEHICLE_CONSTANTS.VEHICLE_NOT_FOUND,
      );
    }

    return ResponseUtil.successResponse(
      res,
      STATUS_CODES.SUCCESS,
      vehicles,
      VEHICLE_CONSTANTS.VEHICLE_DATA_FETCHED,
    );
  } catch (error) {
    ResponseUtil.handleError(res, error);
  }
};

export const getVehicleById = async (req: CustomRequest, res: Response) => {
  try {
    const { id } = req.params;
    const userId = req.userId;
    const vehicle = await Vehicle.findOne({
      _id: id,
      isDeleted: false,
      userId: userId,
    });
    if (!vehicle) {
      return ResponseUtil.errorResponse(
        res,
        STATUS_CODES.SUCCESS,
        VEHICLE_CONSTANTS.VEHICLE_NOT_FOUND,
      );
    }
    // res.status(200).json({ success: true, data: vehicle });
    return ResponseUtil.successResponse(
      res,
      STATUS_CODES.SUCCESS,
      { vehicle },
      VEHICLE_CONSTANTS.VEHICLE_DATA_FETCHED,
    );
  } catch (error) {
    ResponseUtil.handleError(res, error);
  }
};

export const deleteVehicle = async (req: CustomRequest, res: Response) => {
  try {
    const { id } = req.params;
    const userId = req.userId;
    if (!userId) {
      return ResponseUtil.errorResponse(
        res,
        STATUS_CODES.UNAUTHORIZED,
        "Unauthorized",
      );
    }
    const deletedVehicle = await Vehicle.findOneAndUpdate(
      { _id: id, userId, isDeleted: false },
      { isDeleted: true },
      { new: true },
    );
    if (!deletedVehicle) {
      return ResponseUtil.errorResponse(
        res,
        STATUS_CODES.NOT_FOUND,
        VEHICLE_CONSTANTS.VEHICLE_NOT_FOUND,
      );
    }
    return ResponseUtil.successResponse(
      res,
      STATUS_CODES.SUCCESS,
      deletedVehicle,
      VEHICLE_CONSTANTS.VEHICLE_DELETED_SUCCESSFULLY,
    );
  } catch (e) {
    ResponseUtil.handleError(res, e);
  }
};
