import { GoogleSpreadsheet } from 'google-spreadsheet'
import { JWT } from 'google-auth-library'

function setNestedProperty(obj: any, path: string, value: string) {
    const keys = path.split('.')
    let current = obj

    for (let i = 0; i < keys.length - 1; i++) {
        const key = keys[i]
        if (!current[key]) {
            current[key] = {}
        }
        current = current[key]
    }

    const lastKey = keys[keys.length - 1]

    // Check if the key has an array index pattern like "requirements[0]"
    const arrayMatch = lastKey.match(/^([a-z]+)\[(\d+)\]$/i)
    if (arrayMatch) {
        const arrayName = arrayMatch[1]
        const index = parseInt(arrayMatch[2], 10)

        // Initialize array if it doesn't exist
        if (!Array.isArray(current[arrayName])) {
            current[arrayName] = []
        }

        current[arrayName][index] = value
    } else {
        current[lastKey] = value
    }
}

export class GoogleSheets {
    #doc: GoogleSpreadsheet

    constructor(creds: any, docId: string) {
        const jwt = new JWT({
            email: creds.client_email,
            key: creds.private_key,
            scopes: [
                'https://www.googleapis.com/auth/spreadsheets',
                'https://www.googleapis.com/auth/drive.file',
            ],
        });
        this.#doc = new GoogleSpreadsheet(docId, jwt)
    }

    async init() {
        await this.#doc.loadInfo()
    }

    async getI18n(languages: string[]): Promise<Record<string, Record<string, string>>> {
        const i18n: Record<string, Record<string, string>> = {}
        for (const [sheetTitle, sheet] of Object.entries(this.#doc.sheetsByTitle)) {
            if (sheetTitle.includes('result-')) {
                continue
            }
            const rows = await sheet.getRows()
            if (rows.length === 0) continue

            // Get header names from the first row to find the correct column names
            const headerValues = Object.keys(rows[0].toObject())

            for (const row of rows) {
                let root = row.get('Root')
                const key = row.get('Token')
                if (!key) {
                    continue
                }
                if (!root) {
                    root = sheetTitle
                }
                for (const lang of languages) {
                    i18n[lang] = i18n[lang] || {}

                    // Find the column that matches the language (either "locale" or "locale (Country)")
                    const columnName = headerValues.find((header: string) => {
                        const normalizedHeader = header.toLowerCase()
                        return normalizedHeader === lang || normalizedHeader.startsWith(`${lang} (`)
                    })

                    if (columnName && row.get(columnName)) {
                        i18n[lang][`${root}.${key}`.toLowerCase()] = row.get(columnName)
                    }
                }
            }
        }
        return i18n
    }

    async getResultI18n(languages: string[]): Promise<Record<string, Record<string, any>>> {
        const i18n: Record<string, Record<string, any>> = {}
        for (const [sheetTitle, sheet] of Object.entries(this.#doc.sheetsByTitle)) {
            if (!sheetTitle.includes('result-')) {
                continue
            }
            const title = sheetTitle.replace('result-', '')
            const rows = await sheet.getRows()
            if (rows.length === 0) continue

            // Get header names from the first row to find the correct column names
            const headerValues = Object.keys(rows[0].toObject())

            for (const row of rows) {
                const key = row.get('Token')
                if (!key) {
                    continue
                }
                for (const lang of languages) {
                    i18n[lang] = i18n[lang] || {}
                    i18n[lang][title] = i18n[lang][title] || {}

                    // Find the column that matches the language (either "locale" or "locale (Country)")
                    const columnName = headerValues.find((header: string) => {
                        const normalizedHeader = header.toLowerCase()
                        return normalizedHeader === lang || normalizedHeader.startsWith(`${lang} (`)
                    })

                    if (columnName && row.get(columnName)) {
                        setNestedProperty(i18n[lang][title], key.toLowerCase(), row.get(columnName))
                    }
                }
            }
        }
        return i18n
    }

    async syncLocaleToSheet(localeData: Record<string, string>, languages: string[]): Promise<void> {
        // Get all existing keys from all sheets
        const existingKeys = new Set<string>()
        const sheetsByName: Record<string, any> = {}

        for (const [sheetTitle, sheet] of Object.entries(this.#doc.sheetsByTitle)) {
            if (sheetTitle.includes('result-')) {
                continue
            }
            sheetsByName[sheetTitle.toLowerCase()] = sheet
            const rows = await sheet.getRows()

            for (const row of rows) {
                const root = row.get('Root')
                const key = row.get('Token')
                if (key) {
                    const fullKey = root ? `${root}.${key}`.toLowerCase() : `${sheetTitle}.${key}`.toLowerCase()
                    existingKeys.add(fullKey)
                }
            }
        }

        // Find missing keys
        const missingKeys: Array<{ fullKey: string, sheetName: string, token: string, value: string }> = []

        for (const [fullKey, value] of Object.entries(localeData)) {
            const normalizedKey = fullKey.toLowerCase()
            if (!existingKeys.has(normalizedKey)) {
                // Determine sheet name from first word
                const parts = normalizedKey.split('.')
                let sheetName = parts[0]
                let token = parts.slice(1).join('.')

                // If no matching sheet exists, use 'common' sheet
                if (!sheetsByName[sheetName]) {
                    sheetName = 'common'
                    token = normalizedKey
                }

                missingKeys.push({ fullKey: normalizedKey, sheetName, token, value })
            }
        }

        if (missingKeys.length === 0) {
            console.log('No missing keys found')
            return
        }

        console.log(`Found ${missingKeys.length} missing keys, adding to sheets...`)

        // Group missing keys by sheet
        const keysBySheet: Record<string, Array<{ token: string, value: string }>> = {}
        for (const { sheetName, token, value } of missingKeys) {
            if (!keysBySheet[sheetName]) {
                keysBySheet[sheetName] = []
            }
            keysBySheet[sheetName].push({ token, value })
        }

        // Add rows to appropriate sheets
        for (const [sheetName, keys] of Object.entries(keysBySheet)) {
            let sheet = sheetsByName[sheetName]

            // If sheet doesn't exist
            if (!sheet) {
                console.log(`Creating new sheet: ${sheetName}`)
                sheet = await this.#doc.addSheet({
                    title: sheetName,
                    headerValues: ['Root', 'Token', 'en', ...languages.filter(l => l !== 'en')]
                })
                sheetsByName[sheetName] = sheet
            }

            // Get the header row to find column positions
            await sheet.loadHeaderRow()
            const headers = sheet.headerValues

            console.log(`Adding ${keys.length} keys to sheet: ${sheetName}`)

            // Prepare rows to add
            const rowsToAdd = keys.map(({ token, value }) => {
                const row: Record<string, string> = {
                    'Root': '',
                    'Token': token,
                    'EN': value
                }

                return row
            })

            // Add all rows at once
            await sheet.addRows(rowsToAdd)
        }

        console.log('Successfully synced missing keys to sheets')
    }
}
