import { test } from '@japa/runner'
import {
  createCurrencySemantic,
  isCurrencySemantic,
  validateCurrencyConfig,
  DEFAULT_CURRENCY_CONFIG,
  CURRENCY_PRESETS,
  type CurrencyConfig,
} from '../../../../src/modeling/definitions/Currency.js'
import { SemanticType, type AppliedDataSemantic } from '../../../../src/modeling/Semantics.js'

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

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

  test('should merge metadata properly', ({ assert }) => {
    const config: CurrencyConfig = {
      metadata: {
        customField: 'value',
        anotherField: 123,
      },
    }
    const semantic = createCurrencySemantic(config)
    assert.equal(semantic.id, SemanticType.Currency)
    assert.deepEqual(semantic.config?.metadata, config.metadata)
  })

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

  test('should not identify non-currency semantic', ({ assert }) => {
    const fakeSemantic: AppliedDataSemantic = {
      id: SemanticType.Email,
      config: {},
    }
    assert.isFalse(isCurrencySemantic(fakeSemantic))
  })

  test('DEFAULT_CURRENCY_CONFIG should have correct values', ({ assert }) => {
    assert.equal(DEFAULT_CURRENCY_CONFIG.storageFormat, 'decimal')
    assert.equal(DEFAULT_CURRENCY_CONFIG.defaultCurrency, 'USD')
    assert.equal(DEFAULT_CURRENCY_CONFIG.decimalPlaces, 2)
    assert.equal(DEFAULT_CURRENCY_CONFIG.allowNegative, false)
    assert.equal(DEFAULT_CURRENCY_CONFIG.validateCurrencyCode, true)
  })
})

test.group('Currency Storage Formats', () => {
  test('should support decimal storage format', ({ assert }) => {
    const config: CurrencyConfig = {
      storageFormat: 'decimal',
      defaultCurrency: 'USD',
      decimalPlaces: 2,
    }
    const semantic = createCurrencySemantic(config)
    assert.equal(semantic.config?.storageFormat, 'decimal')
    assert.equal(semantic.config?.defaultCurrency, 'USD')
    assert.equal(semantic.config?.decimalPlaces, 2)
  })

  test('should support integer_cents storage format', ({ assert }) => {
    const config: CurrencyConfig = {
      storageFormat: 'integer_cents',
      defaultCurrency: 'EUR',
      decimalPlaces: 2,
    }
    const semantic = createCurrencySemantic(config)
    assert.equal(semantic.config?.storageFormat, 'integer_cents')
    assert.equal(semantic.config?.defaultCurrency, 'EUR')
  })

  test('should support complex_object storage format', ({ assert }) => {
    const config: CurrencyConfig = {
      storageFormat: 'complex_object',
      allowedCurrencies: ['USD', 'EUR', 'GBP'],
      decimalPlaces: 2,
    }
    const semantic = createCurrencySemantic(config)
    assert.equal(semantic.config?.storageFormat, 'complex_object')
    assert.deepEqual(semantic.config?.allowedCurrencies, ['USD', 'EUR', 'GBP'])
  })
})

test.group('Currency Configuration Validation', () => {
  test('should validate that defaultCurrency is required for decimal format', ({ assert }) => {
    const config: CurrencyConfig = {
      storageFormat: 'decimal',
      // Missing defaultCurrency
      decimalPlaces: 2,
    }
    const errors = validateCurrencyConfig(config)
    assert.include(errors, 'defaultCurrency is required when storageFormat is not complex_object')
  })

  test('should validate that defaultCurrency is required for integer_cents format', ({ assert }) => {
    const config: CurrencyConfig = {
      storageFormat: 'integer_cents',
      // Missing defaultCurrency
      decimalPlaces: 2,
    }
    const errors = validateCurrencyConfig(config)
    assert.include(errors, 'defaultCurrency is required when storageFormat is not complex_object')
  })

  test('should not require defaultCurrency for complex_object format', ({ assert }) => {
    const config: CurrencyConfig = {
      storageFormat: 'complex_object',
      // No defaultCurrency needed
      allowedCurrencies: ['USD', 'EUR'],
    }
    const errors = validateCurrencyConfig(config)
    assert.notInclude(errors, 'defaultCurrency is required when storageFormat is not complex_object')
  })

  test('should validate that decimalPlaces is non-negative', ({ assert }) => {
    const config: CurrencyConfig = {
      storageFormat: 'decimal',
      defaultCurrency: 'USD',
      decimalPlaces: -1,
    }
    const errors = validateCurrencyConfig(config)
    assert.include(errors, 'decimalPlaces must be non-negative')
  })

  test('should accept zero decimal places', ({ assert }) => {
    const config: CurrencyConfig = {
      storageFormat: 'decimal',
      defaultCurrency: 'JPY',
      decimalPlaces: 0,
    }
    const errors = validateCurrencyConfig(config)
    assert.notInclude(errors, 'decimalPlaces must be non-negative')
  })

  test('should return empty array for valid config', ({ assert }) => {
    const config: CurrencyConfig = {
      storageFormat: 'decimal',
      defaultCurrency: 'USD',
      decimalPlaces: 2,
      allowNegative: false,
    }
    const errors = validateCurrencyConfig(config)
    assert.lengthOf(errors, 0)
  })

  test('should accumulate multiple validation errors', ({ assert }) => {
    const config: CurrencyConfig = {
      storageFormat: 'decimal',
      // Missing defaultCurrency
      decimalPlaces: -2, // Invalid decimal places
    }
    const errors = validateCurrencyConfig(config)
    assert.lengthOf(errors, 2)
    assert.include(errors, 'defaultCurrency is required when storageFormat is not complex_object')
    assert.include(errors, 'decimalPlaces must be non-negative')
  })
})

test.group('Currency Presets', () => {
  test('USD_DECIMAL preset should have correct configuration', ({ assert }) => {
    const preset = CURRENCY_PRESETS.USD_DECIMAL
    assert.equal(preset.id, SemanticType.Currency)
    assert.equal(preset.config?.storageFormat, 'decimal')
    assert.equal(preset.config?.defaultCurrency, 'USD')
    assert.equal(preset.config?.decimalPlaces, 2)
    assert.equal(preset.config?.allowNegative, false)
  })

  test('USD_CENTS preset should have correct configuration', ({ assert }) => {
    const preset = CURRENCY_PRESETS.USD_CENTS
    assert.equal(preset.id, SemanticType.Currency)
    assert.equal(preset.config?.storageFormat, 'integer_cents')
    assert.equal(preset.config?.defaultCurrency, 'USD')
    assert.equal(preset.config?.decimalPlaces, 2)
    assert.equal(preset.config?.allowNegative, false)
  })

  test('MULTI_CURRENCY preset should have correct configuration', ({ assert }) => {
    const preset = CURRENCY_PRESETS.MULTI_CURRENCY
    assert.equal(preset.id, SemanticType.Currency)
    assert.equal(preset.config?.storageFormat, 'complex_object')
    assert.deepEqual(preset.config?.allowedCurrencies, ['USD', 'EUR', 'GBP', 'JPY', 'CAD'])
    assert.equal(preset.config?.decimalPlaces, 2)
    assert.equal(preset.config?.allowNegative, false)
  })

  test('WITH_NEGATIVES preset should have correct configuration', ({ assert }) => {
    const preset = CURRENCY_PRESETS.WITH_NEGATIVES
    assert.equal(preset.id, SemanticType.Currency)
    assert.equal(preset.config?.storageFormat, 'decimal')
    assert.equal(preset.config?.defaultCurrency, 'USD')
    assert.equal(preset.config?.decimalPlaces, 2)
    assert.equal(preset.config?.allowNegative, true)
  })

  test('HIGH_PRECISION preset should have correct configuration', ({ assert }) => {
    const preset = CURRENCY_PRESETS.HIGH_PRECISION
    assert.equal(preset.id, SemanticType.Currency)
    assert.equal(preset.config?.storageFormat, 'decimal')
    assert.equal(preset.config?.defaultCurrency, 'BTC')
    assert.equal(preset.config?.decimalPlaces, 8)
    assert.equal(preset.config?.allowNegative, false)
  })

  test('all presets should be valid currency semantics', ({ assert }) => {
    Object.values(CURRENCY_PRESETS).forEach((preset) => {
      assert.isTrue(isCurrencySemantic(preset))
      // Validate each preset config
      if (preset.config) {
        const errors = validateCurrencyConfig(preset.config)
        assert.lengthOf(errors, 0, `Preset ${preset.config.storageFormat} should have valid config`)
      }
    })
  })
})

test.group('Currency Currency Configuration', () => {
  test('should support single currency with defaultCurrency', ({ assert }) => {
    const config: CurrencyConfig = {
      storageFormat: 'decimal',
      defaultCurrency: 'EUR',
      decimalPlaces: 2,
    }
    const semantic = createCurrencySemantic(config)
    assert.equal(semantic.config?.defaultCurrency, 'EUR')
    assert.isUndefined(semantic.config?.allowedCurrencies)
  })

  test('should support multiple currencies with allowedCurrencies', ({ assert }) => {
    const currencies = ['USD', 'EUR', 'GBP', 'JPY', 'CAD', 'AUD']
    const config: CurrencyConfig = {
      storageFormat: 'complex_object',
      allowedCurrencies: currencies,
      decimalPlaces: 2,
    }
    const semantic = createCurrencySemantic(config)
    assert.deepEqual(semantic.config?.allowedCurrencies, currencies)
  })

  test('should support currency validation toggle', ({ assert }) => {
    const configWithValidation: CurrencyConfig = {
      storageFormat: 'decimal',
      defaultCurrency: 'USD',
      validateCurrencyCode: true,
    }
    const configWithoutValidation: CurrencyConfig = {
      storageFormat: 'decimal',
      defaultCurrency: 'USD',
      validateCurrencyCode: false,
    }

    const semanticWithValidation = createCurrencySemantic(configWithValidation)
    const semanticWithoutValidation = createCurrencySemantic(configWithoutValidation)

    assert.isTrue(semanticWithValidation.config?.validateCurrencyCode)
    assert.isFalse(semanticWithoutValidation.config?.validateCurrencyCode)
  })
})

test.group('Currency Precision Configuration', () => {
  test('should support standard 2 decimal places (USD, EUR)', ({ assert }) => {
    const config: CurrencyConfig = {
      storageFormat: 'decimal',
      defaultCurrency: 'USD',
      decimalPlaces: 2,
    }
    const semantic = createCurrencySemantic(config)
    assert.equal(semantic.config?.decimalPlaces, 2)
  })

  test('should support zero decimal places (JPY, KRW)', ({ assert }) => {
    const config: CurrencyConfig = {
      storageFormat: 'decimal',
      defaultCurrency: 'JPY',
      decimalPlaces: 0,
    }
    const semantic = createCurrencySemantic(config)
    assert.equal(semantic.config?.decimalPlaces, 0)
  })

  test('should support high precision (3 decimal places for BHD, KWD)', ({ assert }) => {
    const config: CurrencyConfig = {
      storageFormat: 'decimal',
      defaultCurrency: 'BHD',
      decimalPlaces: 3,
    }
    const semantic = createCurrencySemantic(config)
    assert.equal(semantic.config?.decimalPlaces, 3)
  })

  test('should support very high precision (cryptocurrencies)', ({ assert }) => {
    const config: CurrencyConfig = {
      storageFormat: 'decimal',
      defaultCurrency: 'BTC',
      decimalPlaces: 8,
    }
    const semantic = createCurrencySemantic(config)
    assert.equal(semantic.config?.decimalPlaces, 8)
  })
})

test.group('Currency Negative Values Configuration', () => {
  test('should support disallowing negative values (default)', ({ assert }) => {
    const config: CurrencyConfig = {
      storageFormat: 'decimal',
      defaultCurrency: 'USD',
      allowNegative: false,
    }
    const semantic = createCurrencySemantic(config)
    assert.isFalse(semantic.config?.allowNegative)
  })

  test('should support allowing negative values (refunds, discounts)', ({ assert }) => {
    const config: CurrencyConfig = {
      storageFormat: 'decimal',
      defaultCurrency: 'USD',
      allowNegative: true,
    }
    const semantic = createCurrencySemantic(config)
    assert.isTrue(semantic.config?.allowNegative)
  })
})

test.group('Currency Metadata Configuration', () => {
  test('should support custom metadata', ({ assert }) => {
    const metadata = {
      displayFormat: 'currency',
      showCurrencySymbol: true,
      rounding: 'standard',
      customField: 'value',
    }
    const config: CurrencyConfig = {
      storageFormat: 'decimal',
      defaultCurrency: 'USD',
      metadata,
    }
    const semantic = createCurrencySemantic(config)
    assert.deepEqual(semantic.config?.metadata, metadata)
  })

  test('should merge metadata with other config', ({ assert }) => {
    const config: CurrencyConfig = {
      storageFormat: 'complex_object',
      allowedCurrencies: ['USD', 'EUR'],
      decimalPlaces: 2,
      metadata: {
        source: 'external_api',
        lastUpdated: '2023-01-01',
      },
    }
    const semantic = createCurrencySemantic(config)
    assert.equal(semantic.config?.storageFormat, 'complex_object')
    assert.deepEqual(semantic.config?.allowedCurrencies, ['USD', 'EUR'])
    assert.equal(semantic.config?.decimalPlaces, 2)
    assert.deepEqual(semantic.config?.metadata, {
      source: 'external_api',
      lastUpdated: '2023-01-01',
    })
  })
})

test.group('Currency Cross-semantic Type Guards', () => {
  test('should correctly identify different semantic types', ({ assert }) => {
    const currencySemantic = createCurrencySemantic()

    // Mock other semantics for comparison
    const emailSemantic: AppliedDataSemantic = { id: SemanticType.Email }
    const statusSemantic: AppliedDataSemantic = { id: SemanticType.Status }
    const userSemantic: AppliedDataSemantic = { id: SemanticType.User }

    // Currency semantic should only be identified as Currency
    assert.isTrue(isCurrencySemantic(currencySemantic))
    assert.isFalse(isCurrencySemantic(emailSemantic))
    assert.isFalse(isCurrencySemantic(statusSemantic))
    assert.isFalse(isCurrencySemantic(userSemantic))
  })
})

test.group('Currency Real-world Use Cases', () => {
  test('should configure for simple e-commerce store', ({ assert }) => {
    const config: CurrencyConfig = {
      storageFormat: 'decimal',
      defaultCurrency: 'USD',
      decimalPlaces: 2,
      allowNegative: false,
      validateCurrencyCode: true,
    }
    const semantic = createCurrencySemantic(config)
    const errors = validateCurrencyConfig(config)

    assert.lengthOf(errors, 0)
    assert.equal(semantic.config?.storageFormat, 'decimal')
    assert.equal(semantic.config?.defaultCurrency, 'USD')
    assert.isFalse(semantic.config?.allowNegative)
  })

  test('should configure for international marketplace', ({ assert }) => {
    const config: CurrencyConfig = {
      storageFormat: 'complex_object',
      allowedCurrencies: ['USD', 'EUR', 'GBP', 'JPY', 'CAD', 'AUD'],
      decimalPlaces: 2,
      allowNegative: false,
      validateCurrencyCode: true,
    }
    const semantic = createCurrencySemantic(config)
    const errors = validateCurrencyConfig(config)

    assert.lengthOf(errors, 0)
    assert.equal(semantic.config?.storageFormat, 'complex_object')
    assert.isArray(semantic.config?.allowedCurrencies)
    assert.lengthOf(semantic.config?.allowedCurrencies || [], 6)
  })

  test('should configure for financial system with precision', ({ assert }) => {
    const config: CurrencyConfig = {
      storageFormat: 'integer_cents',
      defaultCurrency: 'USD',
      decimalPlaces: 4,
      allowNegative: true,
      validateCurrencyCode: true,
    }
    const semantic = createCurrencySemantic(config)
    const errors = validateCurrencyConfig(config)

    assert.lengthOf(errors, 0)
    assert.equal(semantic.config?.storageFormat, 'integer_cents')
    assert.equal(semantic.config?.decimalPlaces, 4)
    assert.isTrue(semantic.config?.allowNegative)
  })

  test('should configure for cryptocurrency trading', ({ assert }) => {
    const config: CurrencyConfig = {
      storageFormat: 'decimal',
      defaultCurrency: 'BTC',
      decimalPlaces: 8,
      allowNegative: false,
      validateCurrencyCode: false, // Custom crypto currency
    }
    const semantic = createCurrencySemantic(config)
    const errors = validateCurrencyConfig(config)

    assert.lengthOf(errors, 0)
    assert.equal(semantic.config?.defaultCurrency, 'BTC')
    assert.equal(semantic.config?.decimalPlaces, 8)
    assert.isFalse(semantic.config?.validateCurrencyCode)
  })
})
