/**
 * E-Commerce Data Domain Example
 *
 * This file demonstrates how to create a comprehensive e-commerce data domain
 * using the API Client Core modeling system. It follows the business-first approach
 * and demonstrates proper use of namespaces, models, entities, properties,
 * associations, and semantic annotations.
 *
 * The example includes:
 * - User Management domain with proper authentication setup
 * - Product Catalog with categories and inventory
 * - Order Management with cart functionality and payment processing
 * - Proper semantic annotations for security, timestamps, and business logic
 * - Realistic associations between entities with proper cardinality
 *
 * @example
 * ```typescript
 * import { createEcommerceDomain } from './ecommerce-domain.js'
 *
 * const domain = createEcommerceDomain()
 * console.log('Domain created with', domain.graph.nodeCount(), 'nodes')
 * ```
 */

import { DataDomain } from '../../../DataDomain.js'
import { SemanticType } from '../../../Semantics.js'
import { createCalculatedSemantic } from '../../../definitions/Calculated.js'
import { createCurrencySemantic } from '../../../definitions/Currency.js'
import { createSummarySemantic } from '../../../definitions/Summary.js'
import {
  addAvatarUrlField,
  addCreatedAtField,
  addDescriptionField,
  addEmailField,
  addFirstNameField,
  addIdField,
  addLastNameField,
  addNameField,
  addPasswordField,
  addPhoneField,
  addPublicUniqueNameField,
  addUpdatedAtField,
  addSkuField,
  addPriceField,
  addQuantityField,
  addWeightField,
  addImagesField,
  addCustomStatusField,
  addBooleanField,
  addSessionIdField,
  addExpiresAtField,
  addUnitPriceField,
  addCurrencyAmountField,
} from '../../../helpers/Intelisense.js'
import type { CreateTemplateOptions } from '../../types.js'

function createDomain(): DataDomain {
  return new DataDomain({
    info: {
      name: 'E-Commerce Platform',
      displayName: 'E-Commerce Platform',
      description:
        'A comprehensive e-commerce platform with user management, product catalog, and order processing capabilities',
    },
  })
}

/**
 * Creates a comprehensive e-commerce data domain following domain-driven design principles.
 *
 * This function demonstrates the proper hierarchy and organization for a business domain:
 * 1. Creates the root DataDomain
 * 2. Organizes functionality into logical namespaces (User Management, Product Catalog, etc.)
 * 3. Groups related entities into models within each namespace
 * 4. Defines entities with proper semantic annotations
 * 5. Establishes associations with appropriate cardinality and business rules
 *
 * @returns A fully configured DataDomain with all e-commerce entities and relationships
 */
export default function createEcommerceDomain(options: CreateTemplateOptions = {}): DataDomain {
  // Ensure the root data domain
  const domain = options.domain ?? createDomain()

  //
  // 1. USER MANAGEMENT NAMESPACE
  //
  const userManagement = domain.addNamespace({
    info: {
      name: 'UserManagement',
      displayName: 'User Management',
      description: 'Handles user authentication, authorization, and profile management',
    },
  })

  // User Authentication Model
  const authModel = userManagement.addModel({
    info: {
      name: 'Authentication',
      displayName: 'User Authentication',
      description: 'User accounts and authentication data',
    },
  })

  // User Entity - The core user account
  const userEntity = authModel.addEntity({
    info: {
      name: 'User',
      displayName: 'User Account',
      description: 'Represents a user account in the system',
    },
    semantics: [{ id: SemanticType.User }], // Mark this as a User entity for system-wide recognition
  })

  // User properties with proper semantic annotations
  addIdField(userEntity, { displayName: 'User ID', description: 'Unique identifier for the user' })
  addEmailField(userEntity, { description: 'User email address for login and communication' })
  addPasswordField(userEntity, { description: 'User password for authentication' })

  const fName = addFirstNameField(userEntity, { description: 'User first name' })
  fName.required = true

  const lName = addLastNameField(userEntity, { description: 'User last name' })
  lName.required = true

  userEntity.addProperty({
    info: { name: 'role', displayName: 'User Role', description: 'User role for permission management' },
    type: 'string',
    required: true,
    schema: {
      enum: ['customer', 'admin', 'moderator'],
      defaultValue: {
        type: 'literal',
        value: 'customer',
      },
    },
    semantics: [{ id: SemanticType.UserRole }],
  })

  userEntity.addProperty({
    info: {
      name: 'email_verified',
      displayName: 'Email Verified',
      description: 'Whether the user has verified their email address',
    },
    type: 'boolean',
    required: true,
    schema: {
      defaultValue: {
        type: 'literal',
        value: 'false',
      },
    },
  })

  addCreatedAtField(userEntity, { description: 'When the user account was created' })
  addUpdatedAtField(userEntity, { description: 'When the user account was last updated' })

  // User Profile Model
  const profileModel = userManagement.addModel({
    info: {
      name: 'UserProfile',
      displayName: 'User Profile',
      description: 'Extended user profile information',
    },
  })

  // User Profile Entity
  const userProfileEntity = profileModel.addEntity({
    info: {
      name: 'user_profile',
      displayName: 'User Profile',
      description: 'Extended profile information for users',
    },
  })

  addIdField(userProfileEntity, { displayName: 'Profile ID', description: 'Unique identifier for the profile' })

  userProfileEntity.addProperty({
    info: { name: 'birthdate', displayName: 'Date of Birth', description: 'User date of birth' },
    type: 'date',
  })

  addPhoneField(userProfileEntity, { description: 'User phone number' })
  addAvatarUrlField(userProfileEntity, { description: 'URL to user profile picture' })

  // User-Profile Association (One-to-One), not required for registration flows.
  userEntity.addAssociation(
    { key: userProfileEntity.key },
    {
      info: { name: 'profile', displayName: 'User Profile', description: 'Link to extended profile information' },
      required: false,
      multiple: false,
    }
  )

  // Address Entity
  const addressEntity = profileModel.addEntity({
    info: {
      name: 'address',
      displayName: 'Address',
      description: 'Physical address information',
    },
  })

  addIdField(addressEntity, { displayName: 'Address ID', description: 'Unique identifier for the address' })

  addressEntity.addProperty({
    info: { name: 'type', displayName: 'Address Type', description: 'Type of address (shipping, billing, etc.)' },
    type: 'string',
    required: true,
    schema: {
      enum: ['shipping', 'billing', 'both'],
    },
  })

  addressEntity.addProperty({
    info: {
      name: 'line1',
      displayName: 'Address Line 1',
      description: 'Primary street address, including house number and street name',
    },
    type: 'string',
    required: true,
  })

  addressEntity.addProperty({
    info: {
      name: 'line2',
      displayName: 'Address Line 2',
      description: 'Optional field for additional street-level information like apartment numbers or building names.',
    },
    type: 'string',
    required: false,
  })

  addressEntity.addProperty({
    info: {
      name: 'line3',
      displayName: 'Address Line 3',
      description: 'Optional field for additional street-level information like apartment numbers or building names.',
    },
    type: 'string',
    required: false,
  })

  addressEntity.addProperty({
    info: { name: 'city', displayName: 'City', description: 'City name' },
    type: 'string',
    required: true,
  })

  addressEntity.addProperty({
    info: { name: 'state', displayName: 'State/Province', description: 'State or province' },
    type: 'string',
    required: true,
  })

  addressEntity.addProperty({
    info: { name: 'postal_code', displayName: 'ZIP/Postal Code', description: 'ZIP or postal code' },
    type: 'string',
    required: true,
  })

  addressEntity.addProperty({
    info: { name: 'country', displayName: 'Country', description: 'Country name' },
    type: 'string',
    required: true,
  })

  // User-Address Association (One-to-Many)
  userEntity.addAssociation(
    { key: addressEntity.key },
    {
      info: { name: 'addresses', displayName: 'User Addresses', description: 'User shipping and billing addresses' },
      required: false,
      multiple: true,
    }
  )

  //
  // 2. PRODUCT CATALOG NAMESPACE
  //
  const productCatalog = domain.addNamespace({
    info: {
      name: 'ProductCatalog',
      displayName: 'Product Catalog',
      description: 'Product information, categories, and inventory management',
    },
  })

  // Product Management Model
  const productModel = productCatalog.addModel({
    info: {
      name: 'Products',
      displayName: 'Product Management',
      description: 'Core product entities and relationships',
    },
  })

  // Category Entity
  const categoryEntity = productModel.addEntity({
    info: {
      name: 'category',
      displayName: 'Product Category',
      description: 'Product categorization for organization and navigation',
    },
  })
  addIdField(categoryEntity, { displayName: 'Category ID', description: 'Unique identifier for the category' })
  addNameField(categoryEntity, { displayName: 'Category Name', description: 'Display name of the category' })
  addPublicUniqueNameField(categoryEntity, {
    displayName: 'URL Slug',
    description: 'URL-friendly identifier for the category',
  })
  addDescriptionField(categoryEntity, { description: 'Category description' })

  // Self-referencing association for category hierarchy
  categoryEntity.addAssociation(
    { key: categoryEntity.key },
    {
      info: {
        name: 'parent_category',
        displayName: 'Parent Category',
        description: 'Parent category for hierarchical organization',
      },
      required: false,
      multiple: false,
    }
  )

  // Product Entity
  const productEntity = productModel.addEntity({
    info: {
      name: 'product',
      displayName: 'Product',
      description: 'Core product information and specifications',
    },
  })

  addIdField(productEntity, { displayName: 'Product ID', description: 'Unique identifier for the product' })
  addNameField(productEntity, { displayName: 'Product Name', description: 'Display name of the product' })
  addPublicUniqueNameField(productEntity, {
    displayName: 'URL Slug',
    description: 'URL-friendly identifier for the product',
  })
  addDescriptionField(productEntity, { description: 'Detailed product description' })

  productEntity.addProperty({
    info: { name: 'short_description', displayName: 'Short Description', description: 'Brief product summary' },
    type: 'string',
    semantics: [createSummarySemantic()],
  })

  addSkuField(productEntity, { description: 'Stock Keeping Unit - unique product identifier' })

  addPriceField(productEntity, { description: 'Product price in cents (USD)' })

  productEntity.addProperty({
    info: {
      name: 'compare_at_price',
      displayName: 'Compare at Price',
      description: 'Original price for discount display',
    },
    type: 'number',
    schema: {
      maximum: 0,
    },
    bindings: [
      {
        type: 'web',
        schema: {
          dataType: 'float',
        },
      },
    ],
    semantics: [
      createCurrencySemantic({
        storageFormat: 'integer_cents',
        defaultCurrency: 'USD',
        decimalPlaces: 2,
        allowNegative: false,
        validateCurrencyCode: true,
      }),
    ],
  })

  addCustomStatusField(productEntity, ['inactive', 'active', 'pending', 'archived'], {
    description: 'Current status of the product',
  })

  addWeightField(productEntity, { description: 'Product weight in grams' })

  addImagesField(productEntity, { description: 'Array of product image URLs' })

  addCreatedAtField(productEntity, { description: 'When the product was created' })
  addUpdatedAtField(productEntity, { description: 'When the product was last updated' })

  // Product-Category Association (Many-to-Many)
  productEntity.addAssociation(
    { key: categoryEntity.key },
    {
      info: {
        name: 'categories',
        displayName: 'Product Categories',
        description: 'Categories this product belongs to',
      },
      required: true,
      multiple: true,
      semantics: [{ id: SemanticType.Categories }],
    }
  )

  // Inventory Model
  const inventoryModel = productCatalog.addModel({
    info: {
      name: 'Inventory',
      displayName: 'Inventory Management',
      description: 'Stock levels and inventory tracking',
    },
  })

  // Inventory Entity
  const inventoryEntity = inventoryModel.addEntity({
    info: {
      name: 'inventory',
      displayName: 'Inventory',
      description: 'Stock levels and inventory information for products',
    },
  })

  addIdField(inventoryEntity, {
    displayName: 'Inventory ID',
    description: 'Unique identifier for the inventory record',
  })

  addQuantityField(inventoryEntity, { displayName: 'Stock Quantity', description: 'Available stock quantity' })

  const reservedQty = addQuantityField(inventoryEntity, {
    name: 'reserved_quantity',
    displayName: 'Reserved Quantity',
    description: 'Quantity reserved for pending orders',
  })
  reservedQty.schema = {
    defaultValue: {
      type: 'literal',
      value: '0',
    },
    minimum: 0,
  }

  addBooleanField(inventoryEntity, 'trackQuantity', 'true', {
    displayName: 'Track Quantity',
    description: 'Whether to track inventory for this product',
  })

  addBooleanField(inventoryEntity, 'allowBackorder', 'false', {
    displayName: 'Allow Backorder',
    description: 'Whether to allow ordering when out of stock',
  })

  // Product-Inventory Association (One-to-One)
  productEntity.addAssociation(
    { key: inventoryEntity.key },
    {
      info: {
        name: 'inventory',
        displayName: 'Product Inventory',
        description: 'Inventory information for this product',
      },
      required: true,
      multiple: false,
    }
  )

  //
  // 3. ORDER MANAGEMENT NAMESPACE
  //
  const orderManagement = domain.addNamespace({
    info: {
      name: 'OrderManagement',
      displayName: 'Order Management',
      description: 'Shopping cart, orders, and payment processing',
    },
  })

  // Shopping Cart Model
  const cartModel = orderManagement.addModel({
    info: {
      name: 'ShoppingCart',
      displayName: 'Shopping Cart',
      description: 'Shopping cart and cart items',
    },
  })

  // Cart Entity
  const cartEntity = cartModel.addEntity({
    info: {
      name: 'cart',
      displayName: 'Shopping Cart',
      description: 'User shopping cart',
    },
  })

  addIdField(cartEntity, { displayName: 'Cart ID', description: 'Unique identifier for the cart' })

  addSessionIdField(cartEntity, { description: 'Session identifier for guest carts' })

  addExpiresAtField(cartEntity, { description: 'When the cart expires for cleanup' })

  addCreatedAtField(cartEntity, { description: 'When the cart was created' })
  addUpdatedAtField(cartEntity, { description: 'When the cart was last updated' })

  // Cart-User Association (Many-to-One)
  cartEntity.addAssociation(
    { key: userEntity.key },
    {
      info: { name: 'user', displayName: 'Cart Owner', description: 'User who owns this cart' },
      required: false,
      multiple: false,
      semantics: [{ id: SemanticType.ResourceOwnerIdentifier }],
    }
  )

  // Cart Item Entity
  const cartItemEntity = cartModel.addEntity({
    info: {
      name: 'cart_item',
      displayName: 'Cart Item',
      description: 'Individual items in a shopping cart',
    },
  })

  addIdField(cartItemEntity, { displayName: 'Cart Item ID', description: 'Unique identifier for the cart item' })

  const cartItemQuantity = addQuantityField(cartItemEntity, { description: 'Number of items' })
  cartItemQuantity.schema = { minimum: 1 }

  const cartUnitPrice = addUnitPriceField(cartItemEntity, { description: 'Price per unit at time of adding to cart' })

  cartItemEntity.addProperty({
    info: { name: 'total_price', displayName: 'Total Price', description: 'Total price for this line item' },
    type: 'number',
    required: true,
    readOnly: true,
    semantics: [
      createCurrencySemantic({
        storageFormat: 'integer_cents',
        defaultCurrency: 'USD',
        decimalPlaces: 2,
        allowNegative: false,
      }),
      createCalculatedSemantic({
        formula: `{{${cartUnitPrice.key}}} * {{${cartItemQuantity.key}}}`,
        dependencies: [cartUnitPrice.key, cartItemQuantity.key],
        recalculateOnUpdate: true,
      }),
    ],
  })

  // Cart-CartItem Association (One-to-Many)
  cartEntity.addAssociation(
    { key: cartItemEntity.key },
    {
      info: { name: 'items', displayName: 'Cart Items', description: 'Items in this cart' },
      required: false,
      multiple: true,
      onDelete: 'cascade',
    }
  )

  // CartItem-Product Association (Many-to-One)
  cartItemEntity.addAssociation(
    { key: productEntity.key },
    {
      info: { name: 'product', displayName: 'Product', description: 'Product for this cart item' },
      required: true,
      multiple: false,
    }
  )

  // Order Processing Model
  const orderModel = orderManagement.addModel({
    info: {
      name: 'Orders',
      displayName: 'Order Processing',
      description: 'Orders and order fulfillment',
    },
  })

  // Order Entity
  const orderEntity = orderModel.addEntity({
    info: {
      name: 'order',
      displayName: 'Order',
      description: 'Customer order',
    },
  })

  addIdField(orderEntity, { displayName: 'Order ID', description: 'Unique identifier for the order' })

  orderEntity.addProperty({
    info: { name: 'order_number', displayName: 'Order Number', description: 'Human-readable order number' },
    type: 'string',
    required: true,
    unique: true,
  })

  addCustomStatusField(
    orderEntity,
    ['pending', 'confirmed', 'processing', 'shipped', 'delivered', 'cancelled', 'refunded'],
    {
      displayName: 'Order Status',
      description: 'Current status of the order',
    }
  )

  const subtotal = addCurrencyAmountField(orderEntity, 'subtotal', 'Subtotal', {
    description: 'Order subtotal before taxes and shipping',
  })

  const taxAmount = addCurrencyAmountField(orderEntity, 'taxAmount', 'Tax Amount', {
    description: 'Total tax amount',
  })

  const shippingAmount = addCurrencyAmountField(orderEntity, 'shippingAmount', 'Shipping Amount', {
    description: 'Shipping cost',
  })

  orderEntity.addProperty({
    info: { name: 'total_amount', displayName: 'Total Amount', description: 'Final order total' },
    type: 'number',
    required: true,
    semantics: [
      createCurrencySemantic({
        storageFormat: 'integer_cents',
        defaultCurrency: 'USD',
        decimalPlaces: 2,
        allowNegative: false,
      }),
      createCalculatedSemantic({
        formula: `{{${subtotal.key}}} + {{${taxAmount.key}}} + {{${shippingAmount.key}}}`,
        dependencies: [subtotal.key, taxAmount.key, shippingAmount.key],
        recalculateOnUpdate: true,
      }),
    ],
  })

  orderEntity.addProperty({
    info: { name: 'notes', displayName: 'Order Notes', description: 'Customer notes or special instructions' },
    type: 'string',
  })

  addCreatedAtField(orderEntity, { description: 'When the order was created' })
  addUpdatedAtField(orderEntity, { description: 'When the order was last updated' })

  // Order-User Association (Many-to-One)
  orderEntity.addAssociation(
    { key: userEntity.key },
    {
      info: { name: 'customer', displayName: 'Customer', description: 'Customer who placed this order' },
      required: true,
      multiple: false,
      semantics: [{ id: SemanticType.ResourceOwnerIdentifier }],
    }
  )

  // Order-Address Associations (Many-to-One)
  orderEntity.addAssociation(
    { key: addressEntity.key },
    {
      info: { name: 'shipping_address', displayName: 'Shipping Address', description: 'Address for order delivery' },
      required: true,
      multiple: false,
    }
  )

  orderEntity.addAssociation(
    { key: addressEntity.key },
    {
      info: { name: 'billing_address', displayName: 'Billing Address', description: 'Address for billing purposes' },
      required: true,
      multiple: false,
    }
  )

  // Order Line Item Entity
  const orderItemEntity = orderModel.addEntity({
    info: {
      name: 'order_item',
      displayName: 'Order Line Item',
      description: 'Individual line items in an order',
    },
  })

  addIdField(orderItemEntity, { displayName: 'Order Item ID', description: 'Unique identifier for the order item' })

  const quantity = addQuantityField(orderItemEntity, { description: 'Number of items ordered' })
  quantity.schema = { minimum: 1 }

  const unitPrice = addUnitPriceField(orderItemEntity, { description: 'Price per unit at time of order' })

  orderItemEntity.addProperty({
    info: { name: 'total_price', displayName: 'Total Price', description: 'Total price for this line item' },
    type: 'number',
    required: true,
    readOnly: true,
    semantics: [
      createCurrencySemantic({
        storageFormat: 'integer_cents',
        defaultCurrency: 'USD',
        decimalPlaces: 2,
        allowNegative: false,
      }),
      createCalculatedSemantic({
        formula: `{{${quantity.key}}} * {{${unitPrice.key}}}`,
        dependencies: [quantity.key, unitPrice.key],
        recalculateOnUpdate: true,
      }),
    ],
  })

  // orderItemEntity.addProperty({
  //   info: {
  //     name: 'productSnapshot',
  //     displayName: 'Product Snapshot',
  //     description: 'Snapshot of product data at time of order',
  //   },
  //   type: 'object',
  //   required: true,
  //   readOnly: true,
  // })

  // Order-OrderItem Association (One-to-Many)
  orderEntity.addAssociation(
    { key: orderItemEntity.key },
    {
      info: { name: 'items', displayName: 'Order Items', description: 'Items in this order' },
      required: true,
      multiple: true,
      onDelete: 'cascade',
    }
  )

  // OrderItem-Product Association (Many-to-One)
  orderItemEntity.addAssociation(
    { key: productEntity.key },
    {
      info: { name: 'product', displayName: 'Product', description: 'Product for this order item' },
      required: true,
      multiple: false,
    }
  )

  // Payment Model
  const paymentModel = orderManagement.addModel({
    info: {
      name: 'Payments',
      displayName: 'Payment Processing',
      description: 'Payment methods and transactions',
    },
  })

  // Payment Entity
  const paymentEntity = paymentModel.addEntity({
    info: {
      name: 'payment',
      displayName: 'Payment',
      description: 'Payment transaction record',
    },
  })

  addIdField(paymentEntity, { displayName: 'Payment ID', description: 'Unique identifier for the payment' })

  paymentEntity.addProperty({
    info: { name: 'method', displayName: 'Payment Method', description: 'Method used for payment' },
    type: 'string',
    required: true,
    schema: {
      enum: ['credit_card', 'debit_card', 'paypal', 'bank_transfer', 'apple_pay', 'google_pay'],
    },
  })

  addCustomStatusField(paymentEntity, ['pending', 'authorized', 'captured', 'failed', 'cancelled', 'refunded'], {
    displayName: 'Payment Status',
    description: 'Current status of the payment',
  })

  addCurrencyAmountField(paymentEntity, 'amount', 'Payment Amount', { description: 'Amount charged' })

  paymentEntity.addProperty({
    info: { name: 'currency', displayName: 'Currency', description: 'Currency code for the payment' },
    type: 'string',
    required: true,
    schema: {
      defaultValue: {
        type: 'literal',
        value: 'USD',
      },
    },
  })

  paymentEntity.addProperty({
    info: {
      name: 'transaction_id',
      displayName: 'Transaction ID',
      description: 'External payment processor transaction ID',
    },
    type: 'string',
  })

  // paymentEntity.addProperty({
  //   info: {
  //     name: 'gatewayResponse',
  //     displayName: 'Gateway Response',
  //     description: 'Raw response from payment gateway',
  //   },
  //   type: 'object',
  //   readOnly: true,
  // })

  addCreatedAtField(paymentEntity, { description: 'When the payment was created' })

  // Order-Payment Association (One-to-Many)
  orderEntity.addAssociation(
    { key: paymentEntity.key },
    {
      info: { name: 'payments', displayName: 'Order Payments', description: 'Payment transactions for this order' },
      required: false,
      multiple: true,
      onDelete: 'restrict', // Don't allow order deletion if payments exist
    }
  )

  return domain
}
