import fs from 'node:fs'
import path from 'node:path'

// Mock functions to simulate the check-i18n functionality
const vm = require('node:vm')
const transpile = require('typescript').transpile

describe('check-i18n script functionality', () => {
  const testDir = path.join(__dirname, '../i18n-test')
  const testEnDir = path.join(testDir, 'en-US')
  const testZhDir = path.join(testDir, 'zh-Hans')

  // Helper function that replicates the getKeysFromLanguage logic
  async function getKeysFromLanguage(language: string, testPath = testDir): Promise<string[]> {
    return new Promise((resolve, reject) => {
      const folderPath = path.resolve(testPath, language)
      const allKeys: string[] = []

      if (!fs.existsSync(folderPath)) {
        resolve([])
        return
      }

      fs.readdir(folderPath, (err, files) => {
        if (err) {
          reject(err)
          return
        }

        const translationFiles = files.filter(file => /\.(ts|js)$/.test(file))

        translationFiles.forEach((file) => {
          const filePath = path.join(folderPath, file)
          const fileName = file.replace(/\.[^/.]+$/, '')
          const camelCaseFileName = fileName.replace(/[-_](.)/g, (_, c) =>
            c.toUpperCase(),
          )

          try {
            const content = fs.readFileSync(filePath, 'utf8')
            const moduleExports = {}
            const context = {
              exports: moduleExports,
              module: { exports: moduleExports },
              require,
              console,
              __filename: filePath,
              __dirname: folderPath,
            }

            vm.runInNewContext(transpile(content), context)
            const translationObj = (context.module.exports as any).default || context.module.exports

            if (!translationObj || typeof translationObj !== 'object')
              throw new Error(`Error parsing file: ${filePath}`)

            const nestedKeys: string[] = []
            const iterateKeys = (obj: any, prefix = '') => {
              for (const key in obj) {
                const nestedKey = prefix ? `${prefix}.${key}` : key
                if (typeof obj[key] === 'object' && obj[key] !== null && !Array.isArray(obj[key])) {
                  // This is an object (but not array), recurse into it but don't add it as a key
                  iterateKeys(obj[key], nestedKey)
                }
                else {
                  // This is a leaf node (string, number, boolean, array, etc.), add it as a key
                  nestedKeys.push(nestedKey)
                }
              }
            }
            iterateKeys(translationObj)

            const fileKeys = nestedKeys.map(key => `${camelCaseFileName}.${key}`)
            allKeys.push(...fileKeys)
          }
          catch (error) {
            reject(error)
          }
        })
        resolve(allKeys)
      })
    })
  }

  beforeEach(() => {
    // Clean up and create test directories
    if (fs.existsSync(testDir))
      fs.rmSync(testDir, { recursive: true })

    fs.mkdirSync(testDir, { recursive: true })
    fs.mkdirSync(testEnDir, { recursive: true })
    fs.mkdirSync(testZhDir, { recursive: true })
  })

  afterEach(() => {
    // Clean up test files
    if (fs.existsSync(testDir))
      fs.rmSync(testDir, { recursive: true })
  })

  describe('Key extraction logic', () => {
    it('should extract only leaf node keys, not intermediate objects', async () => {
      const testContent = `const translation = {
  simple: 'Simple Value',
  nested: {
    level1: 'Level 1 Value',
    deep: {
      level2: 'Level 2 Value'
    }
  },
  array: ['not extracted'],
  number: 42,
  boolean: true
}

export default translation
`

      fs.writeFileSync(path.join(testEnDir, 'test.ts'), testContent)

      const keys = await getKeysFromLanguage('en-US')

      expect(keys).toEqual([
        'test.simple',
        'test.nested.level1',
        'test.nested.deep.level2',
        'test.array',
        'test.number',
        'test.boolean',
      ])

      // Should not include intermediate object keys
      expect(keys).not.toContain('test.nested')
      expect(keys).not.toContain('test.nested.deep')
    })

    it('should handle camelCase file name conversion correctly', async () => {
      const testContent = `const translation = {
  key: 'value'
}

export default translation
`

      fs.writeFileSync(path.join(testEnDir, 'app-debug.ts'), testContent)
      fs.writeFileSync(path.join(testEnDir, 'user_profile.ts'), testContent)

      const keys = await getKeysFromLanguage('en-US')

      expect(keys).toContain('appDebug.key')
      expect(keys).toContain('userProfile.key')
    })
  })

  describe('Missing keys detection', () => {
    it('should detect missing keys in target language', async () => {
      const enContent = `const translation = {
  common: {
    save: 'Save',
    cancel: 'Cancel',
    delete: 'Delete'
  },
  app: {
    title: 'My App',
    version: '1.0'
  }
}

export default translation
`

      const zhContent = `const translation = {
  common: {
    save: '保存',
    cancel: '取消'
    // missing 'delete'
  },
  app: {
    title: '我的应用'
    // missing 'version'
  }
}

export default translation
`

      fs.writeFileSync(path.join(testEnDir, 'test.ts'), enContent)
      fs.writeFileSync(path.join(testZhDir, 'test.ts'), zhContent)

      const enKeys = await getKeysFromLanguage('en-US')
      const zhKeys = await getKeysFromLanguage('zh-Hans')

      const missingKeys = enKeys.filter(key => !zhKeys.includes(key))

      expect(missingKeys).toContain('test.common.delete')
      expect(missingKeys).toContain('test.app.version')
      expect(missingKeys).toHaveLength(2)
    })
  })

  describe('Extra keys detection', () => {
    it('should detect extra keys in target language', async () => {
      const enContent = `const translation = {
  common: {
    save: 'Save',
    cancel: 'Cancel'
  }
}

export default translation
`

      const zhContent = `const translation = {
  common: {
    save: '保存',
    cancel: '取消',
    delete: '删除', // extra key
    extra: '额外的' // another extra key
  },
  newSection: {
    someKey: '某个值' // extra section
  }
}

export default translation
`

      fs.writeFileSync(path.join(testEnDir, 'test.ts'), enContent)
      fs.writeFileSync(path.join(testZhDir, 'test.ts'), zhContent)

      const enKeys = await getKeysFromLanguage('en-US')
      const zhKeys = await getKeysFromLanguage('zh-Hans')

      const extraKeys = zhKeys.filter(key => !enKeys.includes(key))

      expect(extraKeys).toContain('test.common.delete')
      expect(extraKeys).toContain('test.common.extra')
      expect(extraKeys).toContain('test.newSection.someKey')
      expect(extraKeys).toHaveLength(3)
    })
  })

  describe('File filtering logic', () => {
    it('should filter keys by specific file correctly', async () => {
      // Create multiple files
      const file1Content = `const translation = {
  button: 'Button',
  text: 'Text'
}

export default translation
`

      const file2Content = `const translation = {
  title: 'Title',
  description: 'Description'
}

export default translation
`

      fs.writeFileSync(path.join(testEnDir, 'components.ts'), file1Content)
      fs.writeFileSync(path.join(testEnDir, 'pages.ts'), file2Content)
      fs.writeFileSync(path.join(testZhDir, 'components.ts'), file1Content)
      fs.writeFileSync(path.join(testZhDir, 'pages.ts'), file2Content)

      const allEnKeys = await getKeysFromLanguage('en-US')

      // Test file filtering logic
      const targetFile = 'components'
      const filteredEnKeys = allEnKeys.filter(key =>
        key.startsWith(targetFile.replace(/[-_](.)/g, (_, c) => c.toUpperCase())),
      )

      expect(allEnKeys).toHaveLength(4) // 2 keys from each file
      expect(filteredEnKeys).toHaveLength(2) // only components keys
      expect(filteredEnKeys).toContain('components.button')
      expect(filteredEnKeys).toContain('components.text')
      expect(filteredEnKeys).not.toContain('pages.title')
      expect(filteredEnKeys).not.toContain('pages.description')
    })
  })

  describe('Complex nested structure handling', () => {
    it('should handle deeply nested objects correctly', async () => {
      const complexContent = `const translation = {
  level1: {
    level2: {
      level3: {
        level4: {
          deepValue: 'Deep Value'
        },
        anotherValue: 'Another Value'
      },
      simpleValue: 'Simple Value'
    },
    directValue: 'Direct Value'
  },
  rootValue: 'Root Value'
}

export default translation
`

      fs.writeFileSync(path.join(testEnDir, 'complex.ts'), complexContent)

      const keys = await getKeysFromLanguage('en-US')

      expect(keys).toContain('complex.level1.level2.level3.level4.deepValue')
      expect(keys).toContain('complex.level1.level2.level3.anotherValue')
      expect(keys).toContain('complex.level1.level2.simpleValue')
      expect(keys).toContain('complex.level1.directValue')
      expect(keys).toContain('complex.rootValue')

      // Should not include intermediate objects
      expect(keys).not.toContain('complex.level1')
      expect(keys).not.toContain('complex.level1.level2')
      expect(keys).not.toContain('complex.level1.level2.level3')
      expect(keys).not.toContain('complex.level1.level2.level3.level4')
    })
  })

  describe('Edge cases', () => {
    it('should handle empty objects', async () => {
      const emptyContent = `const translation = {
  empty: {},
  withValue: 'value'
}

export default translation
`

      fs.writeFileSync(path.join(testEnDir, 'empty.ts'), emptyContent)

      const keys = await getKeysFromLanguage('en-US')

      expect(keys).toContain('empty.withValue')
      expect(keys).not.toContain('empty.empty')
    })

    it('should handle special characters in keys', async () => {
      const specialContent = `const translation = {
  'key-with-dash': 'value1',
  'key_with_underscore': 'value2',
  'key.with.dots': 'value3',
  normalKey: 'value4'
}

export default translation
`

      fs.writeFileSync(path.join(testEnDir, 'special.ts'), specialContent)

      const keys = await getKeysFromLanguage('en-US')

      expect(keys).toContain('special.key-with-dash')
      expect(keys).toContain('special.key_with_underscore')
      expect(keys).toContain('special.key.with.dots')
      expect(keys).toContain('special.normalKey')
    })

    it('should handle different value types', async () => {
      const typesContent = `const translation = {
  stringValue: 'string',
  numberValue: 42,
  booleanValue: true,
  nullValue: null,
  undefinedValue: undefined,
  arrayValue: ['array', 'values'],
  objectValue: {
    nested: 'nested value'
  }
}

export default translation
`

      fs.writeFileSync(path.join(testEnDir, 'types.ts'), typesContent)

      const keys = await getKeysFromLanguage('en-US')

      expect(keys).toContain('types.stringValue')
      expect(keys).toContain('types.numberValue')
      expect(keys).toContain('types.booleanValue')
      expect(keys).toContain('types.nullValue')
      expect(keys).toContain('types.undefinedValue')
      expect(keys).toContain('types.arrayValue')
      expect(keys).toContain('types.objectValue.nested')
      expect(keys).not.toContain('types.objectValue')
    })
  })

  describe('Real-world scenario tests', () => {
    it('should handle app-debug structure like real files', async () => {
      const appDebugEn = `const translation = {
  pageTitle: {
    line1: 'Prompt',
    line2: 'Engineering'
  },
  operation: {
    applyConfig: 'Publish',
    resetConfig: 'Reset',
    debugConfig: 'Debug'
  },
  generate: {
    instruction: 'Instructions',
    generate: 'Generate',
    resTitle: 'Generated Prompt',
    noDataLine1: 'Describe your use case on the left,',
    noDataLine2: 'the orchestration preview will show here.'
  }
}

export default translation
`

      const appDebugZh = `const translation = {
  pageTitle: {
    line1: '提示词',
    line2: '编排'
  },
  operation: {
    applyConfig: '发布',
    resetConfig: '重置',
    debugConfig: '调试'
  },
  generate: {
    instruction: '指令',
    generate: '生成',
    resTitle: '生成的提示词',
    noData: '在左侧描述您的用例，编排预览将在此处显示。' // This is extra
  }
}

export default translation
`

      fs.writeFileSync(path.join(testEnDir, 'app-debug.ts'), appDebugEn)
      fs.writeFileSync(path.join(testZhDir, 'app-debug.ts'), appDebugZh)

      const enKeys = await getKeysFromLanguage('en-US')
      const zhKeys = await getKeysFromLanguage('zh-Hans')

      const missingKeys = enKeys.filter(key => !zhKeys.includes(key))
      const extraKeys = zhKeys.filter(key => !enKeys.includes(key))

      expect(missingKeys).toContain('appDebug.generate.noDataLine1')
      expect(missingKeys).toContain('appDebug.generate.noDataLine2')
      expect(extraKeys).toContain('appDebug.generate.noData')

      expect(missingKeys).toHaveLength(2)
      expect(extraKeys).toHaveLength(1)
    })

    it('should handle time structure with operation nested keys', async () => {
      const timeEn = `const translation = {
  months: {
    January: 'January',
    February: 'February'
  },
  operation: {
    now: 'Now',
    ok: 'OK',
    cancel: 'Cancel',
    pickDate: 'Pick Date'
  },
  title: {
    pickTime: 'Pick Time'
  },
  defaultPlaceholder: 'Pick a time...'
}

export default translation
`

      const timeZh = `const translation = {
  months: {
    January: '一月',
    February: '二月'
  },
  operation: {
    now: '此刻',
    ok: '确定',
    cancel: '取消',
    pickDate: '选择日期'
  },
  title: {
    pickTime: '选择时间'
  },
  pickDate: '选择日期', // This is extra - duplicates operation.pickDate
  defaultPlaceholder: '请选择时间...'
}

export default translation
`

      fs.writeFileSync(path.join(testEnDir, 'time.ts'), timeEn)
      fs.writeFileSync(path.join(testZhDir, 'time.ts'), timeZh)

      const enKeys = await getKeysFromLanguage('en-US')
      const zhKeys = await getKeysFromLanguage('zh-Hans')

      const missingKeys = enKeys.filter(key => !zhKeys.includes(key))
      const extraKeys = zhKeys.filter(key => !enKeys.includes(key))

      expect(missingKeys).toHaveLength(0) // No missing keys
      expect(extraKeys).toContain('time.pickDate') // Extra root-level pickDate
      expect(extraKeys).toHaveLength(1)

      // Should have both keys available
      expect(zhKeys).toContain('time.operation.pickDate') // Correct nested key
      expect(zhKeys).toContain('time.pickDate') // Extra duplicate key
    })
  })

  describe('Statistics calculation', () => {
    it('should calculate correct difference statistics', async () => {
      const enContent = `const translation = {
  key1: 'value1',
  key2: 'value2',
  key3: 'value3'
}

export default translation
`

      const zhContentMissing = `const translation = {
  key1: 'value1',
  key2: 'value2'
  // missing key3
}

export default translation
`

      const zhContentExtra = `const translation = {
  key1: 'value1',
  key2: 'value2', 
  key3: 'value3',
  key4: 'extra',
  key5: 'extra2'
}

export default translation
`

      fs.writeFileSync(path.join(testEnDir, 'stats.ts'), enContent)

      // Test missing keys scenario
      fs.writeFileSync(path.join(testZhDir, 'stats.ts'), zhContentMissing)

      const enKeys = await getKeysFromLanguage('en-US')
      const zhKeysMissing = await getKeysFromLanguage('zh-Hans')

      expect(enKeys.length - zhKeysMissing.length).toBe(1) // +1 means 1 missing key

      // Test extra keys scenario
      fs.writeFileSync(path.join(testZhDir, 'stats.ts'), zhContentExtra)

      const zhKeysExtra = await getKeysFromLanguage('zh-Hans')

      expect(enKeys.length - zhKeysExtra.length).toBe(-2) // -2 means 2 extra keys
    })
  })

  describe('Auto-remove multiline key-value pairs', () => {
    // Helper function to simulate removeExtraKeysFromFile logic
    function removeExtraKeysFromFile(content: string, keysToRemove: string[]): string {
      const lines = content.split('\n')
      const linesToRemove: number[] = []

      for (const keyToRemove of keysToRemove) {
        let targetLineIndex = -1
        const linesToRemoveForKey: number[] = []

        // Find the key line (simplified for single-level keys in test)
        for (let i = 0; i < lines.length; i++) {
          const line = lines[i]
          const keyPattern = new RegExp(`^\\s*${keyToRemove}\\s*:`)
          if (keyPattern.test(line)) {
            targetLineIndex = i
            break
          }
        }

        if (targetLineIndex !== -1) {
          linesToRemoveForKey.push(targetLineIndex)

          // Check if this is a multiline key-value pair
          const keyLine = lines[targetLineIndex]
          const trimmedKeyLine = keyLine.trim()

          // If key line ends with ":" (not complete value), it's likely multiline
          if (trimmedKeyLine.endsWith(':') && !trimmedKeyLine.includes('{') && !trimmedKeyLine.match(/:\s*['"`]/)) {
            // Find the value lines that belong to this key
            let currentLine = targetLineIndex + 1
            let foundValue = false

            while (currentLine < lines.length) {
              const line = lines[currentLine]
              const trimmed = line.trim()

              // Skip empty lines
              if (trimmed === '') {
                currentLine++
                continue
              }

              // Check if this line starts a new key (indicates end of current value)
              if (trimmed.match(/^\w+\s*:/))
                break

              // Check if this line is part of the value
              if (trimmed.startsWith('\'') || trimmed.startsWith('"') || trimmed.startsWith('`') || foundValue) {
                linesToRemoveForKey.push(currentLine)
                foundValue = true

                // Check if this line ends the value (ends with quote and comma/no comma)
                if ((trimmed.endsWith('\',') || trimmed.endsWith('",') || trimmed.endsWith('`,')
                     || trimmed.endsWith('\'') || trimmed.endsWith('"') || trimmed.endsWith('`'))
                    && !trimmed.startsWith('//'))
                  break
              }
              else {
                break
              }

              currentLine++
            }
          }

          linesToRemove.push(...linesToRemoveForKey)
        }
      }

      // Remove duplicates and sort in reverse order
      const uniqueLinesToRemove = [...new Set(linesToRemove)].sort((a, b) => b - a)

      for (const lineIndex of uniqueLinesToRemove)
        lines.splice(lineIndex, 1)

      return lines.join('\n')
    }

    it('should remove single-line key-value pairs correctly', () => {
      const content = `const translation = {
  keepThis: 'This should stay',
  removeThis: 'This should be removed',
  alsoKeep: 'This should also stay',
}

export default translation`

      const result = removeExtraKeysFromFile(content, ['removeThis'])

      expect(result).toContain('keepThis: \'This should stay\'')
      expect(result).toContain('alsoKeep: \'This should also stay\'')
      expect(result).not.toContain('removeThis: \'This should be removed\'')
    })

    it('should remove multiline key-value pairs completely', () => {
      const content = `const translation = {
  keepThis: 'This should stay',
  removeMultiline:
    'This is a multiline value that should be removed completely',
  alsoKeep: 'This should also stay',
}

export default translation`

      const result = removeExtraKeysFromFile(content, ['removeMultiline'])

      expect(result).toContain('keepThis: \'This should stay\'')
      expect(result).toContain('alsoKeep: \'This should also stay\'')
      expect(result).not.toContain('removeMultiline:')
      expect(result).not.toContain('This is a multiline value that should be removed completely')
    })

    it('should handle mixed single-line and multiline removals', () => {
      const content = `const translation = {
  keepThis: 'Keep this',
  removeSingle: 'Remove this single line',
  removeMultiline:
    'Remove this multiline value',
  anotherMultiline:
    'Another multiline that spans multiple lines',
  keepAnother: 'Keep this too',
}

export default translation`

      const result = removeExtraKeysFromFile(content, ['removeSingle', 'removeMultiline', 'anotherMultiline'])

      expect(result).toContain('keepThis: \'Keep this\'')
      expect(result).toContain('keepAnother: \'Keep this too\'')
      expect(result).not.toContain('removeSingle:')
      expect(result).not.toContain('removeMultiline:')
      expect(result).not.toContain('anotherMultiline:')
      expect(result).not.toContain('Remove this single line')
      expect(result).not.toContain('Remove this multiline value')
      expect(result).not.toContain('Another multiline that spans multiple lines')
    })

    it('should properly detect multiline vs single-line patterns', () => {
      const multilineContent = `const translation = {
  singleLine: 'This is single line',
  multilineKey:
    'This is multiline',
  keyWithColon: 'Value with: colon inside',
  objectKey: {
    nested: 'value'
  },
}

export default translation`

      // Test that single line with colon in value is not treated as multiline
      const result1 = removeExtraKeysFromFile(multilineContent, ['keyWithColon'])
      expect(result1).not.toContain('keyWithColon:')
      expect(result1).not.toContain('Value with: colon inside')

      // Test that true multiline is handled correctly
      const result2 = removeExtraKeysFromFile(multilineContent, ['multilineKey'])
      expect(result2).not.toContain('multilineKey:')
      expect(result2).not.toContain('This is multiline')

      // Test that object key removal works (note: this is a simplified test)
      // In real scenario, object removal would be more complex
      const result3 = removeExtraKeysFromFile(multilineContent, ['objectKey'])
      expect(result3).not.toContain('objectKey: {')
      // Note: Our simplified test function doesn't handle nested object removal perfectly
      // This is acceptable as it's testing the main multiline string removal functionality
    })

    it('should handle real-world Polish translation structure', () => {
      const polishContent = `const translation = {
  createApp: 'UTWÓRZ APLIKACJĘ',
  newApp: {
    captionAppType: 'Jaki typ aplikacji chcesz stworzyć?',
    chatbotDescription:
      'Zbuduj aplikację opartą na czacie. Ta aplikacja używa formatu pytań i odpowiedzi.',
    agentDescription:
      'Zbuduj inteligentnego agenta, który może autonomicznie wybierać narzędzia.',
    basic: 'Podstawowy',
  },
}

export default translation`

      const result = removeExtraKeysFromFile(polishContent, ['captionAppType', 'chatbotDescription', 'agentDescription'])

      expect(result).toContain('createApp: \'UTWÓRZ APLIKACJĘ\'')
      expect(result).toContain('basic: \'Podstawowy\'')
      expect(result).not.toContain('captionAppType:')
      expect(result).not.toContain('chatbotDescription:')
      expect(result).not.toContain('agentDescription:')
      expect(result).not.toContain('Jaki typ aplikacji')
      expect(result).not.toContain('Zbuduj aplikację opartą na czacie')
      expect(result).not.toContain('Zbuduj inteligentnego agenta')
    })
  })
})
