import { mkdir, readFile, writeFile } from 'node:fs/promises'
import { basename } from 'node:path'
import { join } from 'node:path'
import { config } from '../config.js'
import { CATALOG, type DownloadResource, type ResourceType } from '../data/catalog.js'

export interface CustomResourceInput {
  type: ResourceType
  name: string
  description?: string
  url: string
  /** Pasirinktinis dydis; jei nenurodytas — bandoma nustatyti per HEAD. */
  sizeBytes?: number
}

/**
 * Katalogo servisas: statinis katalogas + naudotojo pridėti („custom")
 * ištekliai. Custom įrašai išsaugomi `storage/custom_catalog.json`, tad
 * išlieka po mazgo perkrovimo.
 */
export class CatalogService {
  private custom: DownloadResource[] = []
  private loaded = false
  private counter = 0
  private readonly file = join(config.storagePath, 'custom_catalog.json')

  private async load(): Promise<void> {
    if (this.loaded) return
    try {
      const raw = await readFile(this.file, 'utf8')
      const parsed = JSON.parse(raw)
      if (Array.isArray(parsed)) this.custom = parsed
    } catch {
      this.custom = []
    }
    this.loaded = true
  }

  private async persist(): Promise<void> {
    await mkdir(config.storagePath, { recursive: true })
    await writeFile(this.file, JSON.stringify(this.custom, null, 2), 'utf8')
  }

  /** Saugus failo pavadinimas iš URL kelio. */
  private deriveFilename(url: URL, type: ResourceType): string {
    let name = decodeURIComponent(basename(url.pathname))
    name = name.replace(/[^A-Za-z0-9._-]/g, '_')
    if (!name || name === '_' || !name.includes('.')) {
      name = `${type}_${Date.now()}.${type === 'map' ? 'pmtiles' : 'zim'}`
    }
    return name
  }

  /** Bando nustatyti dydį per HEAD (nebūtina; klaida – grąžina 0). */
  private async headSize(url: string): Promise<number> {
    try {
      const res = await fetch(url, { method: 'HEAD', redirect: 'follow' })
      const len = Number(res.headers.get('content-length'))
      return Number.isFinite(len) && len > 0 ? len : 0
    } catch {
      return 0
    }
  }

  async list(type?: ResourceType): Promise<DownloadResource[]> {
    await this.load()
    const all = [...CATALOG, ...this.custom]
    return type ? all.filter((r) => r.type === type) : all
  }

  async get(id: string): Promise<DownloadResource | undefined> {
    await this.load()
    return [...CATALOG, ...this.custom].find((r) => r.id === id)
  }

  async add(input: CustomResourceInput): Promise<DownloadResource> {
    await this.load()

    const name = input.name?.trim()
    if (!name) throw new Error('Trūksta pavadinimo / name required')
    if (input.type !== 'wiki' && input.type !== 'map') {
      throw new Error('Netinkamas tipas / invalid type')
    }
    let url: URL
    try {
      url = new URL(input.url)
    } catch {
      throw new Error('Netinkamas URL / invalid URL')
    }
    if (url.protocol !== 'http:' && url.protocol !== 'https:') {
      throw new Error('URL turi būti http(s) / must be http(s)')
    }

    const size = input.sizeBytes && input.sizeBytes > 0
      ? input.sizeBytes
      : await this.headSize(input.url)

    const resource: DownloadResource = {
      id: `custom_${Date.now()}_${++this.counter}`,
      type: input.type,
      name: { lt: name, en: name },
      description: {
        lt: input.description?.trim() || 'Naudotojo pridėtas šaltinis.',
        en: input.description?.trim() || 'User-added source.',
      },
      url: input.url,
      filename: this.deriveFilename(url, input.type),
      sizeBytes: size,
      custom: true,
    }

    this.custom.push(resource)
    await this.persist()
    return resource
  }

  async remove(id: string): Promise<boolean> {
    await this.load()
    const before = this.custom.length
    this.custom = this.custom.filter((r) => r.id !== id)
    if (this.custom.length === before) return false // nerasta arba statinis
    await this.persist()
    return true
  }
}

export const catalogService = new CatalogService()
