import { Printer } from './models/Printer.js'
import { PrinterProfile } from './models/PrinterProfile.js'
import { FilamentProfile } from './models/FilamentProfile.js'
import { Host } from './models/Host.js'
import { Filament } from './models/Filament.js'
import { Material } from './models/Material.js'
import { FilamentSku } from './models/FilamentSku.js'
import { Spool } from './models/Spool'
import { GCodeFile } from './models/GCodeFile'
import { Job } from './models/Job'
import { Product } from './models/Product'
import { ProductCategory } from './models/ProductCategory'
import { ProductSku } from './models/ProductSku'
import { Part } from './models/Part.js'
import { PartSku } from './models/PartSku.js'
import { Vendor } from './models/Vendor'
import { Courier } from './models/Courier'
import { CourierService } from './models/CourierService'
import { File } from './models/File'
import { SubJob } from './models/SubJob'
import { Initial } from './models/Initial'
import { FilamentStock } from './models/FilamentStock'
import { StockEvent } from './models/StockEvent'
import { StockAudit } from './models/StockAudit'
import { PartStock } from './models/PartStock'
import { ProductStock } from './models/ProductStock'
import { StockLocation } from './models/StockLocation'
import { StockTransfer } from './models/StockTransfer'
import { PurchaseOrder } from './models/PurchaseOrder'
import { OrderItem } from './models/OrderItem'
import { Shipment } from './models/Shipment'
import { AuditLog } from './models/AuditLog'
import { User } from './models/User'
import { AppPassword } from './models/AppPassword.js'
import { NoteType } from './models/NoteType'
import { Note } from './models/Note'
import { DocumentSize } from './models/DocumentSize.js'
import { DocumentTemplate } from './models/DocumentTemplate.js'
import { DocumentPrinter } from './models/DocumentPrinter.js'
import { DocumentJob } from './models/DocumentJob.js'
import { TaxRate } from './models/TaxRate.js'
import { TaxRecord } from './models/TaxRecord.js'
import { Invoice } from './models/Invoice.js'
import { Payment } from './models/Payment.js'
import { Client } from './models/Client.js'
import { SalesOrder } from './models/SalesOrder.js'
import { Marketplace } from './models/Marketplace.js'
import { Listing } from './models/Listing.js'
import { ListingVarient } from './models/ListingVarient.js'
import QuestionCircleIcon from '../components/Icons/QuestionCircleIcon'

export const objectModels = [
  Printer,
  PrinterProfile,
  FilamentProfile,
  Host,
  Filament,
  FilamentSku,
  Material,
  Spool,
  GCodeFile,
  Job,
  Product,
  ProductCategory,
  ProductSku,
  Part,
  PartSku,
  Vendor,
  Courier,
  CourierService,
  File,
  SubJob,
  Initial,
  FilamentStock,
  StockEvent,
  StockAudit,
  PartStock,
  ProductStock,
  StockLocation,
  StockTransfer,
  PurchaseOrder,
  OrderItem,
  Shipment,
  AuditLog,
  User,
  AppPassword,
  NoteType,
  Note,
  DocumentSize,
  DocumentTemplate,
  DocumentPrinter,
  DocumentJob,
  TaxRate,
  TaxRecord,
  Invoice,
  Payment,
  Client,
  SalesOrder,
  Marketplace,
  Listing,
  ListingVarient
]

// Re-export individual models for direct access
export {
  Printer,
  PrinterProfile,
  FilamentProfile,
  Host,
  Filament,
  FilamentSku,
  Material,
  Spool,
  GCodeFile,
  Job,
  Product,
  ProductCategory,
  ProductSku,
  Part,
  PartSku,
  Vendor,
  Courier,
  CourierService,
  File,
  SubJob,
  Initial,
  FilamentStock,
  StockEvent,
  StockAudit,
  PartStock,
  ProductStock,
  StockLocation,
  StockTransfer,
  PurchaseOrder,
  OrderItem,
  Shipment,
  AuditLog,
  User,
  AppPassword,
  NoteType,
  Note,
  DocumentSize,
  DocumentTemplate,
  DocumentPrinter,
  DocumentJob,
  TaxRate,
  TaxRecord,
  Invoice,
  Payment,
  Client,
  SalesOrder,
  Marketplace,
  Listing,
  ListingVarient
}

export function getModelByName(name, ignoreCase = false) {
  function formatName(formattedName) {
    if (ignoreCase == true) {
      formattedName = formattedName.toUpperCase()
    }
    return formattedName
  }
  return (
    objectModels.find((meta) => formatName(meta.name) === formatName(name)) || {
      name: 'unknown',
      label: 'Unknown',
      prefix: 'UNK',
      icon: QuestionCircleIcon,
      url: () => '#',
      properties: {}
    }
  )
}

export function getModelProperty(name, property) {
  const model = getModelByName(name)

  if (!model || !model.properties) {
    return undefined
  }

  return model.properties.find((prop) => prop.name == property)
}

export function getModelProperties(name, propertyList) {
  const model = getModelByName(name)

  if (!model || !model.properties) {
    return []
  }

  // If no propertyList is provided, return all properties
  if (!propertyList || propertyList.length === 0) {
    return model.properties
  }

  // Create a map of property names to properties for efficient lookup
  const propertyMap = new Map(
    model.properties.map((property) => [property.name, property])
  )

  // Return properties in the same order as propertyList
  return propertyList
    .map((propertyName) => propertyMap.get(propertyName))
    .filter((property) => property !== undefined)
}

export function getModelByPrefix(prefix) {
  return (
    objectModels.find((meta) => meta.prefix === prefix) || {
      name: 'unknown',
      label: 'Unknown',
      prefix: 'UNK',
      icon: QuestionCircleIcon,
      url: () => '#',
      properties: {}
    }
  )
}

export function searchModelsByLabel(label) {
  return objectModels.filter(
    (meta) =>
      meta.label.toLowerCase().includes(label.toLowerCase()) ||
      meta.labelPlural.toLowerCase().includes(label.toLowerCase())
  )
}

// Utility function to get nested object values
export const getPropertyValue = (obj, path) => {
  if (!obj || !path) return undefined
  if (path.includes('.')) {
    const propertyPath = path.split('.')
    let currentValue = obj
    for (const prop of propertyPath) {
      if (currentValue && typeof currentValue === 'object') {
        currentValue = currentValue[prop]
      } else {
        currentValue = undefined
        break
      }
    }
    return currentValue
  } else {
    return obj[path]
  }
}

export const evaluateVariable = (expression, data) => {
  if (!expression) return false

  // Only treat as an expression if it starts and ends with ()
  const expr = expression.trim()
  if (!(expr.startsWith('(') && expr.endsWith(')'))) return false

  // Remove the outer parentheses
  const innerExpr = expr.slice(1, -1)

  // Helper to evaluate a single condition like 'foo == "bar"' or 'foo.bar == 42' or 'foo == true'
  const evalCondition = (cond, data) => {
    const match = cond.trim().match(/^([a-zA-Z0-9_.]+)\s*==\s*(.+)$/)
    if (!match) return false
    const [, path, valueRaw] = match
    let value
    let raw = valueRaw.trim()
    // Check for quoted string
    if (
      (raw.startsWith('"') && raw.endsWith('"')) ||
      (raw.startsWith("'") && raw.endsWith("'"))
    ) {
      value = raw.slice(1, -1)
    } else if (raw === 'true') {
      value = true
    } else if (raw === 'false') {
      value = false
    } else if (!isNaN(Number(raw))) {
      value = Number(raw)
    } else {
      value = raw
    }
    // Resolve nested property
    const propValue = path
      .split('.')
      .reduce((acc, key) => (acc ? acc[key] : undefined), data)
    return propValue === value
  }

  // Split by '||' first (lowest precedence)
  const orParts = innerExpr.split(/\|\|/)
  for (let orPart of orParts) {
    // Each orPart may have '&&' (higher precedence)
    const andParts = orPart.split(/&&/)
    const andResult = andParts.every((andPart) => evalCondition(andPart, data))
    if (andResult) return true // If any OR group is true, return true
  }
  return false // None of the OR groups were true
}
