import moment, { Moment } from "moment"
import * as momentTimezone from "moment-timezone"
import { messages } from "../constants/messages"
import * as bcrypt from "bcrypt"
import * as nodemailer from "nodemailer"
import { mailConfig } from "../common/emails/config/mail-config"
import { countryStateJson } from "../static-data/country-state.json"
import * as fs from "node:fs"
import process from "node:process"
import { EV } from "./env.values"
import * as path from "path"

/**
 * Check if the input data is empty or not.
 * @param data - The data to be checked for emptiness.
 * @returns True if the data is considered empty, false otherwise.
 */
export const isEmpty = (data: any): boolean => {
  // Return true if the data is null or undefined
  if (data == null) {
    return true
  }

  // Check if the data is a string and includes only empty values
  if (typeof data === "string") {
    return ["", null, "null", undefined].includes(data)
  }

  // Check if the data is an array and has no elements
  if (Array.isArray(data)) {
    return data.length === 0
  }

  // Check if the data is a date object and has no keys
  if (data instanceof Date) {
    return isNaN(data.getTime())
  }

  // Check if the data is an object and has no keys
  if (typeof data === "object") {
    return Object.keys(data).length === 0
  }

  // Default case: return true if the data length is 0
  return data.length === 0
}

/**
 * Generates a new object by removing specified nested keys from the original object.
 *
 * @template T - The type of the original object.
 * @param {T} obj - The original object.
 * @param {string[]} keys - An array of keys to be removed from the original object.
 * @return {T} - A new object with the specified keys removed.
 */
export const generateResponseObject = <T>(obj: T, keys: string[]): T => {
  const result = { ...obj }
  keys.forEach((key) => {
    const nestedKeys = key.split(".")
    let currentObject = result
    for (const k of nestedKeys) {
      if (currentObject[k]) {
        delete currentObject[k]
      }
      currentObject = currentObject[k]
    }
  })
  return result
}

/**
 * Returns the current timestamp using the moment.js library.
 *
 * @return {Moment} The current timestamp.
 */
export const currentTimestamp = (): Moment => {
  return moment()
}

/**
 * Calculates the difference in minutes between the current time and the given timestamp.
 *
 * @param {number} timestamp - The timestamp to calculate the difference from.
 * @return {number} The difference in minutes between the current time and the given timestamp.
 */
export const findMinutes = (timestamp: number): number => {
  const currentTime = moment.utc()

  const parsedTimestamp = moment.unix(timestamp) // Ensure the timestamp is parsed as UTC

  return currentTime.diff(parsedTimestamp, "minutes")
}

/**
 * Returns the current year.
 *
 * @return {number} The current year.
 */
export const getCurrentYear = (): number => {
  return new Date().getFullYear()
}

const getMessage = (type: string, key: string, replaceKeys: any): string => {
  let message = messages[type][key]

  if (!message) {
    console.error(`Message not found for type: ${type}, key: ${key}`)
    return `Message not found: ${type}.${key}`
  }

  if (!isEmpty(replaceKeys)) {
    Object.keys(replaceKeys).forEach((key) => {
      message = message.replace(key, replaceKeys[key])
    })
  }

  return message
}

export const successMessage = (key: string, replaceKeys?: any): string => {
  return getMessage("success", key, replaceKeys)
}

export const errorMessage = (key: string, replaceKeys?: any): string => {
  return getMessage("error", key, replaceKeys)
}

export const validationMessage = (key: string, replaceKeys?: any): string => {
  return getMessage("validation", key, replaceKeys)
}

export const encryptPassword = async (password: string) => {
  const salt = await bcrypt.genSalt()
  return await bcrypt.hash(password, salt)
}

export const sendEmailNotification = async (
  toEmail: string,
  html: any,
  subject: string,
  cc?: string,
  bcc?: string,
  isMarketingEmail?: boolean,
) => {
  try {
    const transporter = nodemailer.createTransport(mailConfig)

    const mailOptions = {
      from: `"Costifys" <${isMarketingEmail ? EV["MARKETING_EMAIL_FROM"] : EV["EMAIL_FROM"]}>`,
      to: toEmail,
      subject: subject,
      html: html,
      cc: cc,
      bcc: bcc,
      attachments: [
        {
          filename: "Logo.png",
          path: path.join(process.cwd(), "public/static-images/Logo.png"),
          cid: "unique@cidLogo",
          contentDisposition: "inline" as const,
        },
      ],
    }

    await transporter.sendMail(mailOptions)
  } catch (error) {
    console.error("Error sending email:", error)
    throw error
  }
}

/**
 * Fetches the city from the provided state and country from the countryStateJson object.
 *
 * @param {string} state - The state to search for in the countryStateJson object.
 * @param {string} country - The country to search for in the countryStateJson object.
 * @return {Array} An array containing the cities matching the provided state, or an empty array if no match is found.
 */
export const fetchCityFromJson = (state: string, country: string) => {
  // Loop through each object in the array
  for (const obj of countryStateJson[country]) {
    // Check if the object has the given province as a key
    if (obj.hasOwnProperty(state)) {
      return obj[state]
    }
  }
  // Return null or an empty array if the province key is not found
  return []
}

/**
 * Creates a folder at the specified path if it does not already exist.
 *
 * @param {string} path - The path of the folder to create.
 * @return {void} This function does not return anything.
 */
export const createFolderIfNotExist = (path: string): void => {
  if (!fs.existsSync(path)) {
    fs.mkdirSync(path, { recursive: true, mode: 0o775 })
  }
}

export const getCurrentEnvironment = () => {
  return process.env.NODE_ENV || "production"
}

export const convertToUtc = (date: string | Date): Date => {
  // If already a Date object, check if it needs timezone conversion
  if (date instanceof Date) {
    // If it's already in UTC (has 'Z' suffix or is already UTC), return as is
    return new Date(date.toISOString())
  }

  // For string dates, assume they are in Asia/Kolkata timezone and convert to UTC
  // This handles cases where frontend sends local time that needs UTC conversion
  return momentTimezone.tz(date, "Asia/Kolkata").utc().toDate()
}

/**
 * Gets the current UTC timestamp as a Date object
 * @returns {Date} Current UTC time
 */
export const getCurrentUtc = (): Date => {
  return moment().utc().toDate()
}

/**
 * Ensures a date is properly treated as UTC
 * @param {Date} date - The date to normalize to UTC
 * @returns {Date} UTC normalized date
 */
export const ensureUtc = (date: Date): Date => {
  return new Date(date.toISOString())
}

/**
 * Converts a date from local timezone (Asia/Kolkata) to UTC
 * Use this when receiving dates from frontend that are in local time
 * @param {string | Date} date - The date in local timezone
 * @returns {Date} UTC date
 */
export const convertLocalToUtc = (date: string | Date): Date => {
  return momentTimezone.tz(date, "Asia/Kolkata").utc().toDate()
}

/**
 * Converts a UTC date to local timezone (Asia/Kolkata)
 * Use this when sending dates to frontend
 * @param {Date} utcDate - The UTC date
 * @returns {Date} Local timezone date
 */
export const convertUtcToLocal = (utcDate: Date): Date => {
  return momentTimezone.tz(utcDate, "Asia/Kolkata").toDate()
}

export const cleanString = (value: string | null | undefined): string => {
  if (!value) return ""

  return value
    .trim()
    .replace(/\s+/g, "_") // replace all spaces with _
    .replace(/[^a-zA-Z0-9._-]/g, "") // remove special characters except . _ -
}

export const cleanFileName = (value: string): string => {
  return value
    .trim()
    .replace(/\s+/g, "_") // replace spaces with _
    .replace(/[^a-zA-Z0-9._-]/g, "") // remove unsafe chars
}

export const decimalTransformer = {
  to: (value: number) => value,
  from: (value: string) => Number(value), // enough
}

export const canFillMIS = (
  lastDay: number,
  misMonth: number, // 1-12
  misYear: number,
): boolean => {
  if (!lastDay) return true

  const today = new Date()

  const currentMonth = today.getMonth() + 1
  const currentYear = today.getFullYear()

  // ✅ Always allow current month
  if (misMonth === currentMonth && misYear === currentYear) {
    return true
  }

  // ✅ Check for previous month
  const prevMonthDate = new Date(currentYear, currentMonth - 2, 1)
  const prevMonth = prevMonthDate.getMonth() + 1
  const prevYear = prevMonthDate.getFullYear()

  if (misMonth === prevMonth && misYear === prevYear) {
    // deadline = next month of MIS
    const deadline = new Date(misYear, misMonth, lastDay)
    return today <= deadline
  }

  // ❌ Older than previous month
  return false
}

export const aiPrompt = (data: any) => {
  const formattedData =
    data instanceof Map
      ? Object.fromEntries(data)
      : data?.output
        ? data.output
        : data

  const prompt = `
Instruction:
Convert the following project tasks into a professional MIS report. It should be refined version that is easy to read and understand.

Rules:
- Use proper action verbs (Developed, Implemented, Fixed, etc.)
- If text is unclear (e.g., random words), keep it unchanged
- Do NOT add generic verbs like "processed", "managed"
- Replace only "mam" or "madam" with "Ma'am"

Important information
1. Do NOT remove any important task or information.
2. Do NOT repeat similar tasks — intelligently group them.
3. Do NOT copy raw text — rewrite it professionally.
4. Keep the report concise, clean, and easy to scan.
5. Highlight meaningful insights, not just activities.
6. Avoid unnecessary verbosity.

Output Format:
- Return ONLY valid JSON object
- object must contain:
  - business_task (HTML string)
  - technical_task (HTML string)
  - personal_task (HTML string)

HTML Rules:
- Use <b> for section titles
- Use <ul> and <li> for bullet points
- Maintain hierarchy like:
  Project → Activity → Tasks

Style guideline:
- Use bullet points
- Keep sentences short and professional
- Avoid raw or casual language
- No emojis
- No unnecessary repetition
- Keep it crisp but complete

Example HTML structure:
<b>Project Name</b>
<p>Activity Name</p>
<ul>
  <li>task one</li>
  <li>task two</li>
</ul>

Return only JSON object. No explanation.

Data:
${
  typeof formattedData === "string"
    ? formattedData
    : JSON.stringify(formattedData, null, 2)
}
`

  return prompt
}
