const path = require('path')
const chalk = require('chalk')
const ora = require('ora')
const { getConfig } = require('../lib/config')
const { normalizePagesListResponse } = require('../lib/pages-cli')
const {
  listContents,
  createContent,
  updateContent,
  listPages,
  createPage,
  updatePage,
} = require('../lib/api')
const {
  parseBlogCsvFile,
  csvRowToContentBody,
  csvRowToPageBody,
  omitUndefined,
  validateBlogCsvColumns,
  isValidContentSlug,
  normalizeContentSlug,
  resolveImagePathRelativeToCsv,
} = require('../lib/blog-csv')
const { resolveImageUrl, isLocalFile } = require('../lib/media')

const ALLOWED_DEVICES = new Set(['iphone', 'ipad', 'android', 'web'])

function parseDevicesOption(listStr) {
  const parts = String(listStr ?? 'web')
    .split(',')
    .map((s) => s.trim().toLowerCase())
    .filter(Boolean)
  const out = []
  for (const p of parts) {
    if (!ALLOWED_DEVICES.has(p)) {
      throw new Error(`Invalid device "${p}". Allowed: iphone, ipad, android, web`)
    }
    out.push(p)
  }
  return out.length ? out : ['web']
}

function readContentSlug(entry) {
  if (!entry || typeof entry !== 'object') return ''
  const s = entry.slug ?? entry.Slug
  return s != null ? String(s).trim() : ''
}

function readContentId(entry) {
  if (!entry || typeof entry !== 'object') return null
  const id = entry.id ?? entry._id
  return id != null ? String(id) : null
}

function readPageSlug(entry) {
  return readContentSlug(entry)
}

function readPageId(entry) {
  return readContentId(entry)
}

async function resolveOgImageUrl(csvPath, row, appId, uploadImages) {
  const raw = String(row.image ?? '').trim()
  if (!raw || !uploadImages) return raw || undefined

  const candidate = resolveImagePathRelativeToCsv(csvPath, raw)
  if (!isLocalFile(candidate)) {
    return candidate
  }

  return resolveImageUrl(candidate, appId, { fallbackToDataUrl: true })
}

async function pagesImportCommand(csvPath, options) {
  const delimiter = options.delimiter || ','
  let devices
  try {
    devices = parseDevicesOption(options.devices)
  } catch (e) {
    console.error(chalk.red(e.message || String(e)))
    process.exit(1)
  }

  const appId = options.appId || getConfig('defaultAppId')
  const upsert = Boolean(options.upsert)
  const dryRun = Boolean(options.dryRun)
  const forceDraft = Boolean(options.draft)
  const uploadImages = options.uploadImages !== false

  if (!appId) {
    console.error(chalk.red('Error: App ID is required. Use --app-id or set defaultAppId in bild.json.'))
    process.exit(1)
  }

  const absCsv = path.resolve(csvPath)
  let rows
  try {
    rows = parseBlogCsvFile(absCsv, { delimiter })
  } catch (e) {
    console.error(chalk.red(`Failed to read or parse CSV: ${e.message}`))
    process.exit(1)
  }

  if (rows.length === 0) {
    console.log(chalk.yellow('No data rows in CSV (after skipping empty lines).'))
    return
  }

  const { ok, missing } = validateBlogCsvColumns(rows[0])
  if (!ok) {
    console.error(
      chalk.red(
        `CSV is missing required columns: ${missing.join(', ')}\n` +
          'Expected headers: slug, title, published_at, excerpt, content, category, tags, author, image, meta_title, meta_description\n' +
          'Optional: page_slug (URL slug for the page when different from slug)',
      ),
    )
    process.exit(1)
  }

  /** @type {Map<string, { id: string }>} */
  let contentsBySlug = new Map()
  /** @type {Map<string, { id: string }>} */
  let pagesBySlug = new Map()

  if (upsert) {
    const spinner = ora(dryRun ? 'Fetching existing contents & pages (dry run)…' : 'Fetching existing contents & pages…').start()
    try {
      const rawContents = await listContents(appId)
      const contentsList = Array.isArray(rawContents) ? rawContents : []
      contentsBySlug = new Map()
      for (const item of contentsList) {
        const slug = readContentSlug(item)
        const id = readContentId(item)
        if (slug && id) contentsBySlug.set(slug, { id })
      }

      const rawPages = await listPages(appId)
      const pagesList = normalizePagesListResponse(rawPages)
      pagesBySlug = new Map()
      for (const item of pagesList) {
        const slug = readPageSlug(item)
        const id = readPageId(item)
        if (slug && id) pagesBySlug.set(slug, { id })
      }

      spinner.succeed(`Loaded ${contentsBySlug.size} content(s), ${pagesBySlug.size} page(s) by slug`)
    } catch (e) {
      spinner.fail(e.message || String(e))
      process.exit(1)
    }
  }

  let createdContent = 0
  let updatedContent = 0
  let createdPages = 0
  let updatedPages = 0
  let failed = 0

  for (let i = 0; i < rows.length; i++) {
    const row = rows[i]
    const rowNum = i + 2
    const label = chalk.gray(`row ${rowNum}`)

    const rawContentSlug = String(row.slug ?? '').trim()
    const rawPageSlug = String(row.page_slug ?? row.slug ?? '').trim()

    if (!rawContentSlug) {
      console.error(chalk.red(`${label}: slug is required`))
      failed++
      continue
    }
    if (!rawPageSlug) {
      console.error(chalk.red(`${label}: page slug is required (slug or page_slug)`))
      failed++
      continue
    }

    const contentSlug = normalizeContentSlug(rawContentSlug)
    const pageSlug = normalizeContentSlug(rawPageSlug)

    if (!isValidContentSlug(contentSlug)) {
      console.error(
        chalk.red(
          `${label}: content slug "${rawContentSlug}" → "${contentSlug}" is invalid (use lowercase kebab-case).`,
        ),
      )
      failed++
      continue
    }
    if (!isValidContentSlug(pageSlug)) {
      console.error(
        chalk.red(`${label}: page slug "${rawPageSlug}" → "${pageSlug}" is invalid (use lowercase kebab-case).`),
      )
      failed++
      continue
    }

    let resolvedOgImageUrl
    try {
      resolvedOgImageUrl = await resolveOgImageUrl(absCsv, row, appId, uploadImages)
    } catch (e) {
      console.error(chalk.red(`${label}: image error (${e.message})`))
      failed++
      continue
    }

    const contentPayload = omitUndefined(csvRowToContentBody(row, { devices, forceDraft }))
    const pageTitle = String(row.title ?? '').trim()
    if (!pageTitle) {
      console.error(chalk.red(`${label}: title is required`))
      failed++
      continue
    }

    if (dryRun) {
      const exC = upsert && contentsBySlug.has(contentSlug)
      const exP = upsert && pagesBySlug.has(pageSlug)
      console.log(
        chalk.blue(
          `${label}: content ${exC ? 'would update' : 'would create'} "${contentSlug}"; page ${exP ? 'would update' : 'would create'} "${pageSlug}"`,
        ),
      )
      if (process.env.BILD_DEBUG === '1') {
        console.log(chalk.dim('  content:'), JSON.stringify(contentPayload, null, 2))
        console.log(
          chalk.dim('  page:'),
          JSON.stringify(
            omitUndefined(
              csvRowToPageBody(row, {
                contentId: '(new-or-existing-content-id)',
                forceDraft,
                resolvedOgImageUrl,
              }),
            ),
            null,
            2,
          ),
        )
      }
      continue
    }

    try {
      let cid
      const existingContent = upsert ? contentsBySlug.get(contentSlug) : null
      if (existingContent) {
        await updateContent(appId, existingContent.id, contentPayload)
        cid = existingContent.id
        updatedContent++
        console.log(chalk.green(`${label}: updated content `) + chalk.bold(contentSlug))
      } else {
        const created = await createContent(appId, contentPayload)
        cid = readContentId(created)
        if (!cid) {
          throw new Error('CMS did not return a content id after create')
        }
        createdContent++
        contentsBySlug.set(contentSlug, { id: cid })
        console.log(chalk.green(`${label}: created content `) + chalk.bold(contentSlug))
      }

      const pagePayload = omitUndefined(
        csvRowToPageBody(row, {
          contentId: cid,
          forceDraft,
          resolvedOgImageUrl,
        }),
      )

      const existingPage = upsert ? pagesBySlug.get(pageSlug) : null
      if (existingPage) {
        await updatePage(appId, existingPage.id, pagePayload)
        updatedPages++
        console.log(chalk.green(`${label}: updated page `) + chalk.bold(pageSlug))
      } else {
        const createdPageRes = await createPage(appId, pagePayload)
        createdPages++
        const pid = readPageId(createdPageRes)
        if (pid) pagesBySlug.set(pageSlug, { id: pid })
        console.log(chalk.green(`${label}: created page `) + chalk.bold(pageSlug))
      }
    } catch (e) {
      failed++
      console.error(chalk.red(`${label}: ${e.message || String(e)}`))
    }
  }

  if (dryRun) {
    console.log(chalk.cyan(`Dry run finished (${rows.length} row(s)).`))
    return
  }

  console.log(
    chalk.bold(
      `\nDone: contents ${createdContent} created, ${updatedContent} updated; pages ${createdPages} created, ${updatedPages} updated; ${failed} failed (of ${rows.length}).`,
    ),
  )
  if (failed > 0) process.exit(1)
}

module.exports = pagesImportCommand
