import chokidar from 'chokidar'
import path from 'path'
import fs from 'fs'
import chalk from 'chalk'
import os from 'os'

const DEFAULT_IGNORED = [
  '**/node_modules/**',
  '**/.git/**',
  '**/dist/**',
  '**/*.json',
  '**/*.md',
  '**/*.lock'
]

const CODE_EXTENSIONS = ['.ts', '.tsx', '.js', '.jsx', '.py', '.go', '.rs', '.java', '.cpp', '.c']

const configPath = fs.existsSync(path.join(process.cwd(), '.docsync', 'config.json'))
  ? path.join(process.cwd(), '.docsync', 'config.json')
  : path.join(os.homedir(), '.docsync', 'config.json')

function getConfig() {
  if (!fs.existsSync(configPath)) return null
  return JSON.parse(fs.readFileSync(configPath, 'utf-8'))
}

function saveConfig(config: any) {
  fs.writeFileSync(configPath, JSON.stringify(config, null, 2))
}

function loadIgnorePatterns(projectPath: string): string[] {
  const ignorePath = path.join(projectPath, '.docsyncignore')
  if (!fs.existsSync(ignorePath)) return []
  return fs.readFileSync(ignorePath, 'utf-8')
    .split('\n')
    .map(l => l.trim())
    .filter(l => l && !l.startsWith('#'))
    .map(l => `**/${l}/**`)
}

function updateFileTimestamp(config: any, filePath: string) {
  if (!config.trackedFiles) config.trackedFiles = {}
  config.trackedFiles[filePath] = new Date().toISOString()
  saveConfig(config)
}

async function refreshSupabaseToken(config: any): Promise<string | null> {
  try {
    const response = await fetch('https://docsync-api.onrender.com/auth/refresh', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ refreshToken: config.refreshToken })
    })
    const data = await response.json()
    if (data.access_token) {
      config.token = data.access_token
      config.refreshToken = data.refresh_token
      saveConfig(config)
      console.log(chalk.gray('Session refreshed.'))
      return data.access_token
    }
    return null
  } catch { return null }
}

async function refreshConfluenceToken(config: any): Promise<string | null> {
  try {
    const response = await fetch('https://docsync-api.onrender.com/auth/confluence/refresh', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ refreshToken: config.confluenceRefreshToken })
    })
    const data = await response.json()
    if (data.access_token) {
      config.confluenceToken = data.access_token
      if (data.refresh_token) config.confluenceRefreshToken = data.refresh_token
      saveConfig(config)
      console.log(chalk.gray('Confluence session refreshed.'))
      return data.access_token
    }
    return null
  } catch { return null }
}

async function refreshGoogleToken(config: any): Promise<string | null> {
  try {
    const response = await fetch('https://docsync-api.onrender.com/auth/google/refresh', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ refreshToken: config.googleRefreshToken })
    })
    const data = await response.json()
    if (data.access_token) {
      config.googleToken = data.access_token
      if (data.refresh_token) config.googleRefreshToken = data.refresh_token
      saveConfig(config)
      console.log(chalk.gray('Google session refreshed.'))
      return data.access_token
    }
    return null
  } catch { return null }
}

async function generateDoc(config: any, apiUrl: string, relativePath: string, code: string): Promise<any> {
  const response = await fetch(`${apiUrl}/docs/generate`, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': `Bearer ${config.token}`
    },
    body: JSON.stringify({
      filePath: relativePath,
      code: Buffer.from(code).toString('utf8').replace(/[\x00-\x1F\x7F]/g, ' '),
      destination: config.destination,
      projectId: config.projectId,
      notionToken: config.notionToken,
      googleToken: config.googleToken,
      confluenceToken: config.confluenceToken,
      parentPageId: config.parentPageId,
      spaceId: config.spaceId,
            slackWebhookUrl: config.slackWebhookUrl,
            githubToken: config.githubToken,
            githubRepo: config.githubRepo,
            linearToken: config.linearToken,
            linearTeamId: config.linearTeamId,
            readmeApiKey: config.readmeApiKey,
            llmProvider: config.llmProvider,
            llmApiKey: config.llmApiKey
    })
  })
  return { response, data: await response.json() }
}

export function startWatcher(projectPath: string, apiUrl: string) {
  let config = getConfig()

  if (!config) {
    console.log(chalk.red('Not configured. Run docsync init first.'))
    process.exit(1)
  }

  const customIgnore = loadIgnorePatterns(projectPath)
  const allIgnored = [...DEFAULT_IGNORED, ...customIgnore]

  if (customIgnore.length > 0) {
    console.log(chalk.gray(`Ignoring ${customIgnore.length} custom pattern(s) from .docsyncignore`))
  }

  console.log(chalk.blue(`\nWatching for changes in: ${projectPath}`))
  console.log(chalk.gray(`Destination: ${config.destination}\n`))

  const watcher = chokidar.watch(projectPath, {
    ignored: allIgnored,
    persistent: true,
    ignoreInitial: true
  })

  watcher.on('change', async (filePath) => {
    const ext = path.extname(filePath)
    if (!CODE_EXTENSIONS.includes(ext)) return

    console.log(chalk.yellow(`File changed: ${filePath}`))

    config = getConfig()

    try {
      const code = fs.readFileSync(filePath, 'utf-8')
      const relativePath = path.relative(projectPath, filePath)

      let { response, data } = await generateDoc(config, apiUrl, relativePath, code)

      if (response.status === 401) {
        console.log(chalk.yellow('Session expired, refreshing...'))
        const newToken = await refreshSupabaseToken(config)
        if (!newToken) {
          console.log(chalk.red('Session expired. Run docsync init to reconnect.'))
          return
        }
        config = getConfig()
        const retry = await generateDoc(config, apiUrl, relativePath, code)
        data = retry.data
      }

      if (data.error?.includes('403') || data.error?.includes('401') || data.error?.includes('authentication') || data.error?.includes('credential')) {
        if (config.destination === 'confluence') {
          console.log(chalk.yellow('Confluence token expired, refreshing...'))
          const newToken = await refreshConfluenceToken(config)
          if (!newToken) {
            console.log(chalk.red('Confluence session expired. Run docsync init to reconnect.'))
            return
          }
          config = getConfig()
          const retry = await generateDoc(config, apiUrl, relativePath, code)
          data = retry.data
        } else if (config.destination === 'googledocs') {
          console.log(chalk.yellow('Google token expired, refreshing...'))
          const newToken = await refreshGoogleToken(config)
          if (!newToken) {
            console.log(chalk.red('Google session expired. Run docsync init to reconnect.'))
            return
          }
          config = getConfig()
          const retry = await generateDoc(config, apiUrl, relativePath, code)
          data = retry.data
        }
      }

      if (data.doc) {
        config = getConfig()
        updateFileTimestamp(config, relativePath)
        const quality = data.quality || 7
        const qualityEmoji = quality >= 8 ? '🟢' : quality >= 6 ? '🟡' : '🔴'
        console.log(chalk.green(`✅ Doc generated: ${relativePath} ${qualityEmoji} Quality: ${quality}/10`))
        console.log(chalk.gray(`   Updated at: ${new Date().toLocaleString()}`))
      } else if (data.error) {
        console.log(chalk.red(`Error: ${data.error}`))
      }
    } catch (error: any) {
      console.log(chalk.red(`Error: ${error.message}`))
    }
  })

  return watcher
}
