import { mkdir, readdir, writeFile } from 'node:fs/promises'
import path from 'node:path'
import { readJson } from './lib.js'

const root = process.cwd()
const sourcesDir = path.join(root, 'sources')
const docsResourcesDir = path.join(root, 'docs', 'resources')

const sourceFiles = (await readdir(sourcesDir))
  .filter((file) => file.endsWith('.json'))
  .sort((a, b) => a.localeCompare(b))

const resources = []

for (const file of sourceFiles) {
  const manifest = await readJson(path.join(sourcesDir, file))
  const packageCatalogPath = path.join(root, 'packages', 'json', manifest.id, 'catalog.json')
  let packageCatalog = null

  try {
    packageCatalog = await readJson(packageCatalogPath)
  } catch {
    packageCatalog = null
  }

  resources.push({ manifest, packageCatalog })
}

await mkdir(docsResourcesDir, { recursive: true })

const index = [
  '# LibreBible Resource Index',
  '',
  'This file is generated from source manifests and package catalogs. Edit `sources/*.json`, package generators, or catalog scripts instead of hand-editing this index.',
  '',
  '## Available Resources',
  '',
  '| Resource | Type | Language | Date / Edition | Source | License | Features | Attachments | Counts | Detail |',
  '| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |',
]

for (const resource of resources) {
  const { manifest, packageCatalog } = resource
  const detailPath = `docs/resources/${manifest.id}.md`
  index.push(
    [
      link(manifest.title, detailPath),
      manifest.resource_type ?? 'resource',
      languageLabel(manifest),
      editionLabel(manifest),
      sourceLabel(manifest),
      licenseLabel(manifest),
      featureLabels(manifest).join(', ') || 'None listed',
      attachmentLabels(manifest).join(', ') || 'None listed',
      countsLabel(packageCatalog),
      link('Details', detailPath),
    ].join(' | ').replace(/^/, '| ').replace(/$/, ' |'),
  )

  await writeFile(
    path.join(docsResourcesDir, `${manifest.id}.md`),
    `${resourceDetailMarkdown(manifest, packageCatalog)}\n`,
    'utf8',
  )
}

await writeFile(path.join(root, 'RESOURCE_INDEX.md'), `${index.join('\n')}\n`, 'utf8')

console.log(`Generated RESOURCE_INDEX.md and ${resources.length} resource detail page(s).`)

function resourceDetailMarkdown(manifest, packageCatalog) {
  const lines = [
    `# ${manifest.title}`,
    '',
    manifest.catalog_display?.summary ?? manifest.translation?.public_description ?? '',
    '',
    '## Identity',
    '',
    `- Resource ID: \`${manifest.id}\``,
    `- Type: ${manifest.resource_type ?? 'resource'}`,
    `- Abbreviation: ${manifest.abbreviation ?? 'n/a'}`,
    `- Short title: ${manifest.short_title ?? manifest.title}`,
    `- Alternate IDs: ${formatList(manifest.alternate_ids)}`,
    `- Language: ${languageLabel(manifest)}`,
    `- Canon: ${manifest.canon?.scope ?? 'n/a'}`,
    '',
    '## Translation',
    '',
    `- Base title: ${manifest.translation?.base_title ?? 'n/a'}`,
    `- Translation year: ${manifest.translation?.translation_year ?? 'n/a'}`,
    `- Edition: ${manifest.translation?.edition ?? 'n/a'}`,
    `- Edition year: ${manifest.translation?.edition_year ?? 'n/a'}`,
    `- Source text basis: ${manifest.translation?.source_text_basis ?? 'n/a'}`,
    `- Tradition: ${manifest.translation?.tradition ?? 'n/a'}`,
    '',
    '## Source',
    '',
    `- Provider: ${manifest.source?.provider ?? 'n/a'}`,
    `- Upstream ID: ${manifest.source?.upstream_id ?? 'n/a'}`,
    `- Upstream page: ${manifest.source?.url ?? 'n/a'}`,
    `- Download URL: ${manifest.source?.download_url ?? 'n/a'}`,
    `- Source format: ${manifest.source?.format ?? 'n/a'}`,
    `- Upstream last updated: ${manifest.source?.upstream_last_updated ?? 'n/a'}`,
    `- Available upstream formats: ${formatList(manifest.source?.available_formats)}`,
    '',
    '## License',
    '',
    `- Name: ${manifest.license?.name ?? 'n/a'}`,
    `- Redistribution allowed: ${manifest.license?.redistribution === true ? 'Yes' : 'No or unknown'}`,
    `- License/source URL: ${manifest.license?.source_license_url ?? 'n/a'}`,
    `- Attribution: ${manifest.license?.attribution ?? 'n/a'}`,
    `- Jurisdiction notes: ${manifest.license?.jurisdiction_notes ?? 'n/a'}`,
    `- Restrictions: ${manifest.license?.restricted_notes ?? 'n/a'}`,
    '',
    '## Contributors',
    '',
    ...contributorsList(manifest),
    '',
    '## Features',
    '',
    ...featuresList(manifest),
    '',
    '## Attachments',
    '',
    ...attachmentsList(manifest),
    '',
    '## Generated Package',
    '',
    `- Package catalog: ${manifest.packages?.json_catalog ?? 'n/a'}`,
    `- Verses JSONL: ${manifest.packages?.verses_jsonl ?? 'n/a'}`,
    `- Strong\'s links JSONL: ${manifest.packages?.strongs_jsonl ?? 'n/a'}`,
    `- Footnotes JSONL: ${manifest.packages?.footnotes_jsonl ?? 'n/a'}`,
    `- Entries JSONL: ${manifest.packages?.entries_jsonl ?? 'n/a'}`,
    `- Source SHA-256: ${packageCatalog?.source_sha256 ?? manifest.checks?.expected_sha256 ?? 'n/a'}`,
    `- Last checked: ${manifest.checks?.last_checked_at ?? 'n/a'}`,
    '',
    '## Counts',
    '',
    `- Books: ${packageCatalog?.counts?.books ?? 'n/a'}`,
    `- Verses: ${packageCatalog?.counts?.verses ?? 'n/a'}`,
    `- Strong\'s links: ${packageCatalog?.counts?.strongs_links ?? 'n/a'}`,
    `- Footnotes: ${packageCatalog?.counts?.footnotes ?? 'n/a'}`,
    `- Entries: ${packageCatalog?.counts?.entries ?? 'n/a'}`,
    `- Commentary entries: ${packageCatalog?.counts?.commentary_entries ?? 'n/a'}`,
    `- Dictionary entries: ${packageCatalog?.counts?.dictionary_entries ?? 'n/a'}`,
    '',
    '## Package Checksums',
    '',
    ...packageFilesList(packageCatalog),
  ]

  return lines.filter((line, index, all) => !(line === '' && all[index - 1] === '')).join('\n')
}

function languageLabel(manifest) {
  if (typeof manifest.language === 'string') return manifest.language
  const parts = [
    manifest.language?.name,
    manifest.language?.code ? `(${manifest.language.code})` : null,
    manifest.language?.dialect ? `- ${manifest.language.dialect}` : null,
  ]
  return parts.filter(Boolean).join(' ') || 'n/a'
}

function editionLabel(manifest) {
  const translationYear = manifest.translation?.translation_year
  const editionYear = manifest.translation?.edition_year
  const edition = manifest.translation?.edition

  if (translationYear && editionYear) return `${translationYear}; ${editionYear} edition`
  if (editionYear) return `${editionYear} edition`
  return edition ?? 'n/a'
}

function sourceLabel(manifest) {
  return manifest.source?.provider ?? 'n/a'
}

function licenseLabel(manifest) {
  const name = manifest.license?.name ?? 'n/a'
  const redistribution = manifest.license?.redistribution === true ? 'redistributable' : 'restricted/unknown'
  return `${name} (${redistribution})`
}

function featureLabels(manifest) {
  return (manifest.features ?? []).map((feature) => feature.label ?? feature.id ?? feature.type).filter(Boolean)
}

function attachmentLabels(manifest) {
  return [
    ...(manifest.attachments?.included ?? []).map((attachment) => attachment.label ?? attachment.id),
    ...(manifest.attachments?.supported_future_types ?? []).map((type) => `future: ${type}`),
  ].filter(Boolean)
}

function countsLabel(packageCatalog) {
  if (!packageCatalog?.counts) return 'n/a'
  const counts = packageCatalog.counts
  const parts = [
    counts.books != null ? `${counts.books} books` : null,
    counts.verses != null ? `${counts.verses} verses` : null,
    counts.strongs_links != null ? `${counts.strongs_links} Strong's links` : null,
    counts.footnotes != null ? `${counts.footnotes} footnotes` : null,
    counts.commentary_entries != null ? `${counts.commentary_entries} commentary entries` : null,
    counts.dictionary_entries != null ? `${counts.dictionary_entries} dictionary entries` : null,
    counts.entries != null && counts.commentary_entries == null && counts.dictionary_entries == null ? `${counts.entries} entries` : null,
  ]
  return parts.filter(Boolean).join(', ')
}

function contributorsList(manifest) {
  const contributors = manifest.contributors ?? []
  if (contributors.length === 0) return ['- None listed.']
  return contributors.map((contributor) => `- ${contributor.name}: ${contributor.role}`)
}

function featuresList(manifest) {
  const features = manifest.features ?? []
  if (features.length === 0) return ['- None listed.']
  return features.map((feature) => {
    const details = [
      feature.type,
      feature.languages?.length ? `languages: ${feature.languages.join(', ')}` : null,
      feature.systems?.length ? `systems: ${feature.systems.join(', ')}` : null,
      feature.format ? `format: ${feature.format}` : null,
      feature.embedded === true ? 'embedded' : null,
      feature.package ? `package: ${feature.package}` : null,
    ].filter(Boolean)
    return `- ${feature.label ?? feature.id}: ${details.join('; ') || 'listed'}`
  })
}

function attachmentsList(manifest) {
  const included = manifest.attachments?.included ?? []
  const future = manifest.attachments?.supported_future_types ?? []
  const lines = []

  if (included.length > 0) {
    lines.push('Included:')
    for (const attachment of included) {
      lines.push(
        `- ${attachment.label ?? attachment.id}: ${[
          attachment.resource_type,
          attachment.relationship,
          attachment.anchor_types?.length ? `anchors: ${attachment.anchor_types.join(', ')}` : null,
          attachment.languages?.length ? `languages: ${attachment.languages.join(', ')}` : null,
          attachment.systems?.length ? `systems: ${attachment.systems.join(', ')}` : null,
          attachment.package ? `package: ${attachment.package}` : null,
        ].filter(Boolean).join('; ')}`,
      )
    }
  } else {
    lines.push('- No included attachments listed.')
  }

  if (future.length > 0) {
    lines.push('')
    lines.push('Supported future attachment types:')
    for (const type of future) {
      lines.push(`- ${type}`)
    }
  }

  return lines
}

function packageFilesList(packageCatalog) {
  const files = packageCatalog?.files
  if (!files) return ['- No package file checksums listed.']
  return Object.entries(files).map(([key, value]) => `- ${key}: \`${value.path}\` (${value.sha256})`)
}

function formatList(values) {
  return values?.length ? values.join(', ') : 'n/a'
}

function link(label, href) {
  return `[${label}](${href})`
}
