import { test } from '@japa/runner'
import {
  createPasswordSemantic,
  isPasswordSemantic,
  DEFAULT_PASSWORD_CONFIG,
  PasswordEncryptionAlgorithm,
  type PasswordConfig,
} from '../../../../src/modeling/definitions/Password.js'
import { SemanticType, type AppliedDataSemantic } from '../../../../src/modeling/Semantics.js'

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

  test('should create semantic with custom config', ({ assert }) => {
    const config: PasswordConfig = {
      requireSpecialChars: false,
      requireNumbers: false,
      maxAge: 90,
      preventReuse: true,
      encryptionAlgorithm: PasswordEncryptionAlgorithm.Argon2,
      saltRounds: 16,
    }
    const semantic = createPasswordSemantic(config)
    assert.equal(semantic.id, SemanticType.Password)
    assert.deepEqual(semantic.config, { ...DEFAULT_PASSWORD_CONFIG, ...config })
  })

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

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

  test('should preserve custom properties', ({ assert }) => {
    const config: PasswordConfig = {
      customPattern: '^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)[a-zA-Z\\d@$!%*?&]{8,}$',
      customErrorMessage: 'Password must be at least 8 characters',
      customProperty: 'custom value',
    }
    const semantic = createPasswordSemantic(config)
    assert.equal(semantic.config?.customPattern, config.customPattern)
    assert.equal(semantic.config?.customErrorMessage, config.customErrorMessage)
    assert.equal(semantic.config?.customProperty, 'custom value')
  })

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

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

  test('DEFAULT_PASSWORD_CONFIG should have correct values', ({ assert }) => {
    assert.equal(DEFAULT_PASSWORD_CONFIG.requireSpecialChars, true)
    assert.equal(DEFAULT_PASSWORD_CONFIG.requireNumbers, true)
    assert.equal(DEFAULT_PASSWORD_CONFIG.requireUppercase, true)
    assert.equal(DEFAULT_PASSWORD_CONFIG.requireLowercase, true)
    assert.equal(DEFAULT_PASSWORD_CONFIG.maxAge, undefined)
    assert.equal(DEFAULT_PASSWORD_CONFIG.preventReuse, false)
    assert.equal(DEFAULT_PASSWORD_CONFIG.preventReuseCount, 5)
    assert.equal(DEFAULT_PASSWORD_CONFIG.allowCommonPasswords, false)
  })
})

test.group('Password Encryption Algorithms', () => {
  test('should support bcrypt algorithm', ({ assert }) => {
    const config: PasswordConfig = {
      encryptionAlgorithm: PasswordEncryptionAlgorithm.Bcrypt,
      saltRounds: 10,
    }
    const semantic = createPasswordSemantic(config)
    assert.equal(semantic.config?.encryptionAlgorithm, PasswordEncryptionAlgorithm.Bcrypt)
    assert.equal(semantic.config?.saltRounds, 10)
  })

  test('should support argon2 algorithm', ({ assert }) => {
    const config: PasswordConfig = {
      encryptionAlgorithm: PasswordEncryptionAlgorithm.Argon2,
    }
    const semantic = createPasswordSemantic(config)
    assert.equal(semantic.config?.encryptionAlgorithm, PasswordEncryptionAlgorithm.Argon2)
  })

  test('should support scrypt algorithm', ({ assert }) => {
    const config: PasswordConfig = {
      encryptionAlgorithm: PasswordEncryptionAlgorithm.Scrypt,
    }
    const semantic = createPasswordSemantic(config)
    assert.equal(semantic.config?.encryptionAlgorithm, PasswordEncryptionAlgorithm.Scrypt)
  })

  test('PasswordEncryptionAlgorithm enum should have correct values', ({ assert }) => {
    assert.equal(PasswordEncryptionAlgorithm.Bcrypt, 'bcrypt')
    assert.equal(PasswordEncryptionAlgorithm.Argon2, 'argon2')
    assert.equal(PasswordEncryptionAlgorithm.Scrypt, 'scrypt')
  })
})

test.group('Password Validation Requirements', () => {
  test('should configure character requirements', ({ assert }) => {
    const config: PasswordConfig = {
      requireSpecialChars: true,
      requireNumbers: true,
      requireUppercase: false,
      requireLowercase: false,
    }
    const semantic = createPasswordSemantic(config)
    assert.equal(semantic.config?.requireSpecialChars, true)
    assert.equal(semantic.config?.requireNumbers, true)
    assert.equal(semantic.config?.requireUppercase, false)
    assert.equal(semantic.config?.requireLowercase, false)
  })

  test('should configure custom pattern', ({ assert }) => {
    const customPattern = '^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)[a-zA-Z\\d]{8,}$'
    const config: PasswordConfig = {
      customPattern,
      customErrorMessage: 'Invalid password format',
    }
    const semantic = createPasswordSemantic(config)
    assert.equal(semantic.config?.customPattern, customPattern)
    assert.equal(semantic.config?.customErrorMessage, 'Invalid password format')
  })

  test('should configure common password policy', ({ assert }) => {
    const config: PasswordConfig = {
      allowCommonPasswords: true,
    }
    const semantic = createPasswordSemantic(config)
    assert.equal(semantic.config?.allowCommonPasswords, true)
  })
})

test.group('Password Age and Reuse Policies', () => {
  test('should configure password expiration', ({ assert }) => {
    const config: PasswordConfig = {
      maxAge: 30,
    }
    const semantic = createPasswordSemantic(config)
    assert.equal(semantic.config?.maxAge, 30)
  })

  test('should configure password reuse prevention', ({ assert }) => {
    const config: PasswordConfig = {
      preventReuse: true,
      preventReuseCount: 10,
    }
    const semantic = createPasswordSemantic(config)
    assert.equal(semantic.config?.preventReuse, true)
    assert.equal(semantic.config?.preventReuseCount, 10)
  })

  test('should handle undefined maxAge', ({ assert }) => {
    const config: PasswordConfig = {
      maxAge: undefined,
    }
    const semantic = createPasswordSemantic(config)
    assert.equal(semantic.config?.maxAge, undefined)
  })
})

test.group('Password Salt Rounds Configuration', () => {
  test('should configure salt rounds for bcrypt', ({ assert }) => {
    const config: PasswordConfig = {
      encryptionAlgorithm: PasswordEncryptionAlgorithm.Bcrypt,
      saltRounds: 15,
    }
    const semantic = createPasswordSemantic(config)
    assert.equal(semantic.config?.saltRounds, 15)
  })

  test('should use default salt rounds when not specified', ({ assert }) => {
    const config: PasswordConfig = {
      encryptionAlgorithm: PasswordEncryptionAlgorithm.Bcrypt,
    }
    const semantic = createPasswordSemantic(config)
    assert.equal(semantic.config?.saltRounds, DEFAULT_PASSWORD_CONFIG.saltRounds)
  })

  test('should handle low salt rounds', ({ assert }) => {
    const config: PasswordConfig = {
      saltRounds: 4,
    }
    const semantic = createPasswordSemantic(config)
    assert.equal(semantic.config?.saltRounds, 4)
  })

  test('should handle high salt rounds', ({ assert }) => {
    const config: PasswordConfig = {
      saltRounds: 20,
    }
    const semantic = createPasswordSemantic(config)
    assert.equal(semantic.config?.saltRounds, 20)
  })
})

test.group('Password Metadata and Custom Properties', () => {
  test('should handle complex metadata', ({ assert }) => {
    const metadata = {
      source: 'user_registration',
      lastUpdated: '2025-06-23T00:00:00Z',
      complexity: {
        score: 85,
        level: 'high',
      },
      tags: ['secure', 'enterprise'],
    }
    const config: PasswordConfig = {
      metadata,
    }
    const semantic = createPasswordSemantic(config)
    assert.deepEqual(semantic.config?.metadata, metadata)
  })

  test('should preserve metadata when merging configs', ({ assert }) => {
    const metadata = {
      customField: 'value',
      nested: {
        property: 'test',
      },
    }
    const config: PasswordConfig = {
      requireSpecialChars: false,
      metadata,
    }
    const semantic = createPasswordSemantic(config)
    assert.equal(semantic.config?.requireSpecialChars, false)
    assert.deepEqual(semantic.config?.metadata, metadata)
  })

  test('should handle additional properties via index signature', ({ assert }) => {
    const config: PasswordConfig = {
      customValidationFunction: 'validatePasswordStrength',
      applicationSpecific: {
        domain: 'example.com',
        version: '1.0',
      },
      isProduction: true,
    }
    const semantic = createPasswordSemantic(config)
    assert.equal(semantic.config?.customValidationFunction, 'validatePasswordStrength')
    assert.deepEqual(semantic.config?.applicationSpecific, {
      domain: 'example.com',
      version: '1.0',
    })
    assert.equal(semantic.config?.isProduction, true)
  })
})

test.group('Password Configuration Edge Cases', () => {
  test('should handle null config gracefully', ({ assert }) => {
    const semantic = createPasswordSemantic()
    assert.equal(semantic.id, SemanticType.Password)
    assert.isObject(semantic.config)
  })

  test('should override default config completely when specified', ({ assert }) => {
    const config: PasswordConfig = {
      requireSpecialChars: false,
      requireNumbers: false,
      requireUppercase: false,
      requireLowercase: false,
      encryptionAlgorithm: PasswordEncryptionAlgorithm.Scrypt,
      saltRounds: 8,
      maxAge: 365,
      preventReuse: true,
      preventReuseCount: 3,
      allowCommonPasswords: true,
    }
    const semantic = createPasswordSemantic(config)

    // Verify all properties are overridden
    assert.equal(semantic.config?.requireSpecialChars, false)
    assert.equal(semantic.config?.requireNumbers, false)
    assert.equal(semantic.config?.requireUppercase, false)
    assert.equal(semantic.config?.requireLowercase, false)
    assert.equal(semantic.config?.encryptionAlgorithm, PasswordEncryptionAlgorithm.Scrypt)
    assert.equal(semantic.config?.saltRounds, 8)
    assert.equal(semantic.config?.maxAge, 365)
    assert.equal(semantic.config?.preventReuse, true)
    assert.equal(semantic.config?.preventReuseCount, 3)
    assert.equal(semantic.config?.allowCommonPasswords, true)
  })

  test('should handle partial config override', ({ assert }) => {
    const config: PasswordConfig = {
      requireSpecialChars: false,
      maxAge: 60,
    }
    const semantic = createPasswordSemantic(config)

    // Verify specified properties are overridden
    assert.equal(semantic.config?.requireSpecialChars, false)
    assert.equal(semantic.config?.maxAge, 60)

    // Verify unspecified properties use defaults
    assert.equal(semantic.config?.requireNumbers, DEFAULT_PASSWORD_CONFIG.requireNumbers)
    assert.equal(semantic.config?.encryptionAlgorithm, DEFAULT_PASSWORD_CONFIG.encryptionAlgorithm)
    assert.equal(semantic.config?.saltRounds, DEFAULT_PASSWORD_CONFIG.saltRounds)
  })

  test('should handle zero values correctly', ({ assert }) => {
    const config: PasswordConfig = {
      saltRounds: 0,
      maxAge: 0,
      preventReuseCount: 0,
    }
    const semantic = createPasswordSemantic(config)
    assert.equal(semantic.config?.saltRounds, 0)
    assert.equal(semantic.config?.maxAge, 0)
    assert.equal(semantic.config?.preventReuseCount, 0)
  })

  test('should handle empty string values', ({ assert }) => {
    const config: PasswordConfig = {
      customPattern: '',
      customErrorMessage: '',
    }
    const semantic = createPasswordSemantic(config)
    assert.equal(semantic.config?.customPattern, '')
    assert.equal(semantic.config?.customErrorMessage, '')
  })
})
