import { test } from '@japa/runner'
import { SemanticType } from '../../../src/modeling/Semantics.js'

// Import semantic configuration modules that exist
import {
  createGeospatialCoordinatesSemantic,
  isGeospatialCoordinatesSemantic,
  type GeospatialCoordinatesConfig,
  DEFAULT_GEOSPATIAL_CONFIG,
  GeospatialCoordinateFormat,
  GeospatialDistanceUnit,
  GeospatialSpatialReferenceSystem,
} from '../../../src/modeling/definitions/GeospatialCoordinates.js'

import {
  createPasswordSemantic,
  isPasswordSemantic,
  type PasswordConfig,
  DEFAULT_PASSWORD_CONFIG,
} from '../../../src/modeling/definitions/Password.js'

import {
  createStatusSemantic,
  isStatusSemantic,
  type StatusConfig,
  DEFAULT_STATUS_CONFIG,
} from '../../../src/modeling/definitions/Status.js'

import {
  createPublicUniqueNameSemantic,
  isPublicUniqueNameSemantic,
  type PublicUniqueNameConfig,
  DEFAULT_PUBLIC_UNIQUE_NAME_CONFIG,
} from '../../../src/modeling/definitions/PublicUniqueName.js'

import {
  createMarkdownSemantic,
  isMarkdownSemantic,
  type MarkdownConfig,
  DEFAULT_MARKDOWN_CONFIG,
} from '../../../src/modeling/definitions/Markdown.js'

import {
  createHTMLSemantic,
  isHTMLSemantic,
  type HTMLConfig,
  DEFAULT_HTML_CONFIG,
} from '../../../src/modeling/definitions/HTML.js'

import {
  createEmailSemantic,
  isEmailSemantic,
  type EmailConfig,
  DEFAULT_EMAIL_CONFIG,
} from '../../../src/modeling/definitions/Email.js'

import {
  createPhoneSemantic,
  isPhoneSemantic,
  type PhoneConfig,
  DEFAULT_PHONE_CONFIG,
} from '../../../src/modeling/definitions/Phone.js'

import {
  createURLSemantic,
  isURLSemantic,
  type URLConfig,
  DEFAULT_URL_CONFIG,
} from '../../../src/modeling/definitions/URL.js'

import {
  createCurrencySemantic,
  isCurrencySemantic,
  type CurrencyConfig,
  DEFAULT_CURRENCY_CONFIG,
  CURRENCY_PRESETS,
} from '../../../src/modeling/definitions/Currency.js'

import {
  createSKUSemantic,
  isSKUSemantic,
  type SKUConfig,
  DEFAULT_SKU_CONFIG,
  SKU_PRESETS,
} from '../../../src/modeling/definitions/SKU.js'

import {
  createDescriptionSemantic,
  isDescriptionSemantic,
  type DescriptionConfig,
  DEFAULT_DESCRIPTION_CONFIG,
} from '../../../src/modeling/definitions/Description.js'

import {
  createSummarySemantic,
  isSummarySemantic,
  type SummaryConfig,
  DEFAULT_SUMMARY_CONFIG,
} from '../../../src/modeling/definitions/Summary.js'

import {
  createCalculatedSemantic,
  isCalculatedSemantic,
  type CalculatedConfig,
} from '../../../src/modeling/definitions/Calculated.js'

import {
  createDerivedSemantic,
  isDerivedSemantic,
  type DerivedConfig,
} from '../../../src/modeling/definitions/Derived.js'

import {
  createTagsSemantic,
  isTagsSemantic,
  type TagsConfig,
  DEFAULT_TAGS_CONFIG,
} from '../../../src/modeling/definitions/Tags.js'

import {
  createCategoriesSemantic,
  isCategoriesSemantic,
  type CategoriesConfig,
  DEFAULT_CATEGORIES_CONFIG,
} from '../../../src/modeling/definitions/Categories.js'

test.group('Semantic Configurations', () => {
  test('GeospatialCoordinates: should create semantic with default config', ({ assert }) => {
    const semantic = createGeospatialCoordinatesSemantic()
    assert.equal(semantic.id, SemanticType.GeospatialCoordinates)
    assert.deepEqual(semantic.config, DEFAULT_GEOSPATIAL_CONFIG)
  })

  test('GeospatialCoordinates: should create semantic with custom config', ({ assert }) => {
    const config: GeospatialCoordinatesConfig = {
      format: GeospatialCoordinateFormat.LatLon,
      defaultDistanceUnit: GeospatialDistanceUnit.Kilometers,
      spatialReferenceSystem: GeospatialSpatialReferenceSystem.WGS84,
    }
    const semantic = createGeospatialCoordinatesSemantic(config)
    assert.equal(semantic.id, SemanticType.GeospatialCoordinates)
    assert.deepEqual(semantic.config, { ...DEFAULT_GEOSPATIAL_CONFIG, ...config })
  })

  test('GeospatialCoordinates: should identify geospatial semantic', ({ assert }) => {
    const semantic = createGeospatialCoordinatesSemantic()
    assert.isTrue(isGeospatialCoordinatesSemantic(semantic))
  })

  test('Password: should create semantic with default config', ({ assert }) => {
    const semantic = createPasswordSemantic()
    assert.equal(semantic.id, SemanticType.Password)
    assert.deepEqual(semantic.config, DEFAULT_PASSWORD_CONFIG)
  })

  test('Password: should create semantic with custom config', ({ assert }) => {
    const config: PasswordConfig = {
      minLength: 12,
      requireUppercase: true,
      requireNumbers: true,
    }
    const semantic = createPasswordSemantic(config)
    assert.equal(semantic.id, SemanticType.Password)
    assert.deepEqual(semantic.config, { ...DEFAULT_PASSWORD_CONFIG, ...config })
  })

  test('Password: should identify password semantic', ({ assert }) => {
    const semantic = createPasswordSemantic()
    assert.isTrue(isPasswordSemantic(semantic))
  })

  test('Status: should create semantic with default config', ({ assert }) => {
    const semantic = createStatusSemantic()
    assert.equal(semantic.id, SemanticType.Status)
    assert.deepEqual(semantic.config, DEFAULT_STATUS_CONFIG)
  })

  test('Status: should create semantic with custom config', ({ assert }) => {
    const config: StatusConfig = {
      allowedStates: ['active', 'inactive'],
      defaultState: 'active',
    }
    const semantic = createStatusSemantic(config)
    assert.equal(semantic.id, SemanticType.Status)
    assert.deepEqual(semantic.config, { ...DEFAULT_STATUS_CONFIG, ...config })
  })

  test('Status: should identify status semantic', ({ assert }) => {
    const semantic = createStatusSemantic()
    assert.isTrue(isStatusSemantic(semantic))
  })

  test('PublicUniqueName: should create semantic with default config', ({ assert }) => {
    const semantic = createPublicUniqueNameSemantic()
    assert.equal(semantic.id, SemanticType.PublicUniqueName)
    assert.deepEqual(semantic.config, DEFAULT_PUBLIC_UNIQUE_NAME_CONFIG)
  })

  test('PublicUniqueName: should create semantic with custom config', ({ assert }) => {
    const config: PublicUniqueNameConfig = {
      sourceField: 'title',
      separator: '-',
      maxLength: 50,
    }
    const semantic = createPublicUniqueNameSemantic(config)
    assert.equal(semantic.id, SemanticType.PublicUniqueName)
    assert.deepEqual(semantic.config, { ...DEFAULT_PUBLIC_UNIQUE_NAME_CONFIG, ...config })
  })

  test('PublicUniqueName: should identify public unique name semantic', ({ assert }) => {
    const semantic = createPublicUniqueNameSemantic()
    assert.isTrue(isPublicUniqueNameSemantic(semantic))
  })

  test('Markdown: should create semantic with default config', ({ assert }) => {
    const semantic = createMarkdownSemantic()
    assert.equal(semantic.id, SemanticType.Markdown)
    assert.deepEqual(semantic.config, DEFAULT_MARKDOWN_CONFIG)
  })

  test('Markdown: should create semantic with custom config', ({ assert }) => {
    const config: MarkdownConfig = {
      renderToHTML: true,
      allowedTags: ['p', 'h1', 'h2'],
      sanitize: true,
    }
    const semantic = createMarkdownSemantic(config)
    assert.equal(semantic.id, SemanticType.Markdown)
    assert.deepEqual(semantic.config, { ...DEFAULT_MARKDOWN_CONFIG, ...config })
  })

  test('Markdown: should identify markdown semantic', ({ assert }) => {
    const semantic = createMarkdownSemantic()
    assert.isTrue(isMarkdownSemantic(semantic))
  })

  test('HTML: should create semantic with default config', ({ assert }) => {
    const semantic = createHTMLSemantic()
    assert.equal(semantic.id, SemanticType.HTML)
    assert.deepEqual(semantic.config, DEFAULT_HTML_CONFIG)
  })

  test('HTML: should create semantic with custom config', ({ assert }) => {
    const config: HTMLConfig = {
      renderAsHTML: true,
      allowedTags: ['p', 'div', 'span'],
      sanitize: true,
    }
    const semantic = createHTMLSemantic(config)
    assert.equal(semantic.id, SemanticType.HTML)
    assert.deepEqual(semantic.config, { ...DEFAULT_HTML_CONFIG, ...config })
  })

  test('HTML: should identify HTML semantic', ({ assert }) => {
    const semantic = createHTMLSemantic()
    assert.isTrue(isHTMLSemantic(semantic))
  })

  test('Email: should create semantic with default config', ({ assert }) => {
    const semantic = createEmailSemantic()
    assert.equal(semantic.id, SemanticType.Email)
    assert.deepEqual(semantic.config, DEFAULT_EMAIL_CONFIG)
  })

  test('Email: should create semantic with custom config', ({ assert }) => {
    const config: EmailConfig = {
      allowedDomains: ['example.com'],
      requireVerification: true,
    }
    const semantic = createEmailSemantic(config)
    assert.equal(semantic.id, SemanticType.Email)
    assert.deepEqual(semantic.config, { ...DEFAULT_EMAIL_CONFIG, ...config })
  })

  test('Email: should identify email semantic', ({ assert }) => {
    const semantic = createEmailSemantic()
    assert.isTrue(isEmailSemantic(semantic))
  })

  test('Phone: should create semantic with default config', ({ assert }) => {
    const semantic = createPhoneSemantic()
    assert.equal(semantic.id, SemanticType.Phone)
    assert.deepEqual(semantic.config, DEFAULT_PHONE_CONFIG)
  })

  test('Phone: should create semantic with custom config', ({ assert }) => {
    const config: PhoneConfig = {
      allowedCountries: ['US', 'CA'],
      format: 'international',
      requireVerification: true,
    }
    const semantic = createPhoneSemantic(config)
    assert.equal(semantic.id, SemanticType.Phone)
    assert.deepEqual(semantic.config, { ...DEFAULT_PHONE_CONFIG, ...config })
  })

  test('Phone: should identify phone semantic', ({ assert }) => {
    const semantic = createPhoneSemantic()
    assert.isTrue(isPhoneSemantic(semantic))
  })

  test('URL: should create semantic with default config', ({ assert }) => {
    const semantic = createURLSemantic()
    assert.equal(semantic.id, SemanticType.URL)
    assert.deepEqual(semantic.config, DEFAULT_URL_CONFIG)
  })

  test('URL: should create semantic with custom config', ({ assert }) => {
    const config: URLConfig = {
      allowedDomains: ['example.com'],
      requireHTTPS: true,
    }
    const semantic = createURLSemantic(config)
    assert.equal(semantic.id, SemanticType.URL)
    assert.deepEqual(semantic.config, { ...DEFAULT_URL_CONFIG, ...config })
  })

  test('URL: should identify URL semantic', ({ assert }) => {
    const semantic = createURLSemantic()
    assert.isTrue(isURLSemantic(semantic))
  })

  test('Currency: should create semantic with default config', ({ assert }) => {
    const semantic = createCurrencySemantic()
    assert.equal(semantic.id, SemanticType.Currency)
    assert.deepEqual(semantic.config, DEFAULT_CURRENCY_CONFIG)
  })

  test('Currency: should create semantic with custom config', ({ assert }) => {
    const config: CurrencyConfig = {
      storageFormat: 'complex_object',
      allowedCurrencies: ['USD', 'EUR'],
      decimalPlaces: 4,
      allowNegative: true,
    }
    const semantic = createCurrencySemantic(config)
    assert.equal(semantic.id, SemanticType.Currency)
    assert.deepEqual(semantic.config, { ...DEFAULT_CURRENCY_CONFIG, ...config })
  })

  test('Currency: should identify currency semantic', ({ assert }) => {
    const semantic = createCurrencySemantic()
    assert.isTrue(isCurrencySemantic(semantic))
  })

  test('SKU: should create semantic with default config', ({ assert }) => {
    const semantic = createSKUSemantic()
    assert.equal(semantic.id, SemanticType.SKU)
    assert.deepEqual(semantic.config, DEFAULT_SKU_CONFIG)
  })

  test('SKU: should create semantic with custom config', ({ assert }) => {
    const config: SKUConfig = {
      validationMode: 'lenient',
      caseMode: 'lowercase',
      prefix: 'SKU-',
      enforceUniqueness: false,
    }
    const semantic = createSKUSemantic(config)
    assert.equal(semantic.id, SemanticType.SKU)
    assert.deepEqual(semantic.config, { ...DEFAULT_SKU_CONFIG, ...config })
  })

  test('SKU: should identify SKU semantic', ({ assert }) => {
    const semantic = createSKUSemantic()
    assert.isTrue(isSKUSemantic(semantic))
  })

  test('Description: should create semantic with default config', ({ assert }) => {
    const semantic = createDescriptionSemantic()
    assert.equal(semantic.id, SemanticType.Description)
    assert.deepEqual(semantic.config, DEFAULT_DESCRIPTION_CONFIG)
  })

  test('Description: should create semantic with custom config', ({ assert }) => {
    const config: DescriptionConfig = {
      minLength: 10,
      maxLength: 1000,
      allowMarkdown: true,
    }
    const semantic = createDescriptionSemantic(config)
    assert.equal(semantic.id, SemanticType.Description)
    assert.deepEqual(semantic.config, { ...DEFAULT_DESCRIPTION_CONFIG, ...config })
  })

  test('Description: should identify description semantic', ({ assert }) => {
    const semantic = createDescriptionSemantic()
    assert.isTrue(isDescriptionSemantic(semantic))
  })

  test('Summary: should create semantic with default config', ({ assert }) => {
    const semantic = createSummarySemantic()
    assert.equal(semantic.id, SemanticType.Summary)
    assert.deepEqual(semantic.config, DEFAULT_SUMMARY_CONFIG)
  })

  test('Summary: should create semantic with custom config', ({ assert }) => {
    const config: SummaryConfig = {
      metadata: { customField: 'value' },
    }
    const semantic = createSummarySemantic(config)
    assert.equal(semantic.id, SemanticType.Summary)
    assert.deepEqual(semantic.config, { ...DEFAULT_SUMMARY_CONFIG, ...config })
  })

  test('Summary: should identify summary semantic', ({ assert }) => {
    const semantic = createSummarySemantic()
    assert.isTrue(isSummarySemantic(semantic))
  })

  test('Calculated: should create semantic with custom config', ({ assert }) => {
    const config: CalculatedConfig = {
      formula: 'price * quantity',
      dependencies: ['price', 'quantity'],
      recalculateOnUpdate: true,
      allowManualOverride: true,
    }
    const semantic = createCalculatedSemantic(config)
    assert.equal(semantic.id, SemanticType.Calculated)
    assert.deepEqual(semantic.config, config)
  })

  test('Calculated: should identify calculated semantic', ({ assert }) => {
    const config: CalculatedConfig = { formula: 'price * quantity' }
    const semantic = createCalculatedSemantic(config)
    assert.isTrue(isCalculatedSemantic(semantic))
  })

  test('Derived: should create semantic with custom config', ({ assert }) => {
    const config: DerivedConfig = {
      sourceFields: ['firstName', 'lastName'],
      derivationRule: 'concat',
      updateOnChange: true,
      allowManualOverride: true,
      recalculateOnUpdate: false,
    }
    const semantic = createDerivedSemantic(config)
    assert.equal(semantic.id, SemanticType.Derived)
    assert.deepEqual(semantic.config, config)
  })

  test('Derived: should identify derived semantic', ({ assert }) => {
    const config: DerivedConfig = { sourceFields: ['firstName', 'lastName'] }
    const semantic = createDerivedSemantic(config)
    assert.isTrue(isDerivedSemantic(semantic))
  })

  test('Tags: should create semantic with default config', ({ assert }) => {
    const semantic = createTagsSemantic()
    assert.equal(semantic.id, SemanticType.Tags)
    assert.deepEqual(semantic.config, DEFAULT_TAGS_CONFIG)
  })

  test('Tags: should create semantic with custom config', ({ assert }) => {
    const config: TagsConfig = {
      maxTags: 10,
      allowCustomTags: true,
      predefinedTags: ['urgent', 'important'],
    }
    const semantic = createTagsSemantic(config)
    assert.equal(semantic.id, SemanticType.Tags)
    assert.deepEqual(semantic.config, { ...DEFAULT_TAGS_CONFIG, ...config })
  })

  test('Tags: should identify tags semantic', ({ assert }) => {
    const semantic = createTagsSemantic()
    assert.isTrue(isTagsSemantic(semantic))
  })

  test('Categories: should create semantic with default config', ({ assert }) => {
    const semantic = createCategoriesSemantic()
    assert.equal(semantic.id, SemanticType.Categories)
    assert.deepEqual(semantic.config, DEFAULT_CATEGORIES_CONFIG)
  })

  test('Categories: should create semantic with custom config', ({ assert }) => {
    const config: CategoriesConfig = {
      maxCategories: 5,
      allowMultiple: true,
      predefinedCategories: ['feature', 'bug', 'enhancement'],
    }
    const semantic = createCategoriesSemantic(config)
    assert.equal(semantic.id, SemanticType.Categories)
    assert.deepEqual(semantic.config, { ...DEFAULT_CATEGORIES_CONFIG, ...config })
  })

  test('Categories: should identify categories semantic', ({ assert }) => {
    const semantic = createCategoriesSemantic()
    assert.isTrue(isCategoriesSemantic(semantic))
  })

  test('Currency: should create semantic with default config', ({ assert }) => {
    const semantic = createCurrencySemantic()
    assert.equal(semantic.id, SemanticType.Currency)
    assert.deepEqual(semantic.config, DEFAULT_CURRENCY_CONFIG)
  })

  test('Currency: should create semantic with custom config', ({ assert }) => {
    const config: CurrencyConfig = {
      storageFormat: 'complex_object',
      allowedCurrencies: ['USD', 'EUR'],
      decimalPlaces: 4,
      allowNegative: true,
    }
    const semantic = createCurrencySemantic(config)
    assert.equal(semantic.id, SemanticType.Currency)
    assert.deepEqual(semantic.config, { ...DEFAULT_CURRENCY_CONFIG, ...config })
  })

  test('Currency: should identify currency semantic', ({ assert }) => {
    const semantic = createCurrencySemantic()
    assert.isTrue(isCurrencySemantic(semantic))
  })

  test('Currency: presets should be valid', ({ assert }) => {
    Object.values(CURRENCY_PRESETS).forEach((preset) => {
      assert.isTrue(isCurrencySemantic(preset))
      assert.equal(preset.id, SemanticType.Currency)
    })
  })

  test('SKU: presets should be valid', ({ assert }) => {
    Object.values(SKU_PRESETS).forEach((preset) => {
      assert.isTrue(isSKUSemantic(preset))
      assert.equal(preset.id, SemanticType.SKU)
    })
  })

  test('Cross-semantic type guards: should correctly identify different semantic types', ({ assert }) => {
    const emailSemantic = createEmailSemantic()
    const phoneSemantic = createPhoneSemantic()
    const statusSemantic = createStatusSemantic()
    const currencySemantic = createCurrencySemantic()
    const skuSemantic = createSKUSemantic()

    // Each should only be identified as its own type
    assert.isTrue(isEmailSemantic(emailSemantic))
    assert.isFalse(isEmailSemantic(phoneSemantic))
    assert.isFalse(isEmailSemantic(statusSemantic))
    assert.isFalse(isEmailSemantic(currencySemantic))
    assert.isFalse(isEmailSemantic(skuSemantic))

    assert.isFalse(isPhoneSemantic(emailSemantic))
    assert.isTrue(isPhoneSemantic(phoneSemantic))
    assert.isFalse(isPhoneSemantic(statusSemantic))
    assert.isFalse(isPhoneSemantic(currencySemantic))
    assert.isFalse(isPhoneSemantic(skuSemantic))

    assert.isFalse(isStatusSemantic(emailSemantic))
    assert.isFalse(isStatusSemantic(phoneSemantic))
    assert.isTrue(isStatusSemantic(statusSemantic))
    assert.isFalse(isStatusSemantic(currencySemantic))
    assert.isFalse(isStatusSemantic(skuSemantic))

    assert.isFalse(isCurrencySemantic(emailSemantic))
    assert.isFalse(isCurrencySemantic(phoneSemantic))
    assert.isFalse(isCurrencySemantic(statusSemantic))
    assert.isTrue(isCurrencySemantic(currencySemantic))
    assert.isFalse(isCurrencySemantic(skuSemantic))

    assert.isFalse(isSKUSemantic(emailSemantic))
    assert.isFalse(isSKUSemantic(phoneSemantic))
    assert.isFalse(isSKUSemantic(statusSemantic))
    assert.isFalse(isSKUSemantic(currencySemantic))
    assert.isTrue(isSKUSemantic(skuSemantic))
  })
})
