import {
  mkdirSync,
  readFileSync,
  writeFileSync,
  appendFileSync,
  existsSync,
} from 'node:fs'
import { dirname, join } from 'node:path'

const POLLER_STATE_FILE = 'geelib-poller-state.json'
const INBOX_FILE = 'inbox.jsonl'
const META_FILE = 'meta.json'

function ensureDir(path) {
  mkdirSync(path, { recursive: true })
}

function readJson(file, fallback) {
  try {
    return JSON.parse(readFileSync(file, 'utf8'))
  } catch {
    return fallback
  }
}

function writeJsonAtomic(file, data) {
  ensureDir(dirname(file))
  writeFileSync(file, `${JSON.stringify(data, null, 2)}\n`, { mode: 0o600 })
}

function defaultMeta() {
  return { cursor: 0, lastPollAt: 0 }
}

function defaultDiscovered() {
  return {
    configured_sub_ids: [],
    projects: [],
  }
}

function defaultPollerState() {
  return {
    lastPollAt: 0,
    processedMatterIds: [],
    notifiedMatterIds: [],
    activeMatterId: null,
    pendingQueue: [],
    discovered: defaultDiscovered(),
  }
}

/** 将旧版单项目 discovered 结构迁移为 projects 数组。 */
function normalizeDiscovered(raw) {
  if (!raw || typeof raw !== 'object') {
    return defaultDiscovered()
  }
  if (Array.isArray(raw.projects) && raw.projects.length > 0) {
    return {
      configured_sub_ids: Array.isArray(raw.configured_sub_ids)
        ? raw.configured_sub_ids.map(Number)
        : [],
      projects: raw.projects.map(p => ({
        sub_id: p.sub_id ?? null,
        type_id: p.type_id ?? null,
        poll_status_ids: Array.isArray(p.poll_status_ids)
          ? p.poll_status_ids.map(Number)
          : [],
        in_progress_status_id: p.in_progress_status_id ?? null,
        self_test_status_id: p.self_test_status_id ?? null,
      })),
    }
  }
  if (raw.sub_id !== null && raw.sub_id !== undefined) {
    return {
      configured_sub_ids: [],
      projects: [
        {
          sub_id: raw.sub_id,
          type_id: raw.type_id ?? null,
          poll_status_ids: Array.isArray(raw.poll_status_ids)
            ? raw.poll_status_ids.map(Number)
            : Array.isArray(raw.pending_status_ids)
              ? raw.pending_status_ids.map(Number)
              : [],
          in_progress_status_id: raw.in_progress_status_id ?? null,
          self_test_status_id:
            raw.self_test_status_id ?? raw.done_status_id ?? null,
        },
      ],
    }
  }
  return defaultDiscovered()
}

/** Connector 侧状态：轮询去重 + 入站 inbox */
export class GeelibStateStore {
  constructor(stateDir) {
    this.stateDir = stateDir
    this.pollerFile = join(stateDir, POLLER_STATE_FILE)
    this.inboxFile = join(stateDir, INBOX_FILE)
    this.metaFile = join(stateDir, META_FILE)
    ensureDir(stateDir)
    if (!existsSync(this.pollerFile)) {
      this.savePollerState(defaultPollerState())
    }
    if (!existsSync(this.metaFile)) {
      this.saveMeta(defaultMeta())
    }
  }

  loadPollerState() {
    const parsed = readJson(this.pollerFile, defaultPollerState())
    const activeRaw = parsed.activeMatterId
    return {
      lastPollAt: parsed.lastPollAt ?? 0,
      processedMatterIds: Array.isArray(parsed.processedMatterIds)
        ? parsed.processedMatterIds.map(Number)
        : [],
      notifiedMatterIds: Array.isArray(parsed.notifiedMatterIds)
        ? parsed.notifiedMatterIds.map(Number)
        : [],
      activeMatterId:
        activeRaw === null || activeRaw === undefined
          ? null
          : Number(activeRaw),
      pendingQueue: Array.isArray(parsed.pendingQueue)
        ? parsed.pendingQueue.map(q => ({
            matterId: Number(q.matterId),
            subId: Number(q.subId),
            typeId: Number(q.typeId),
            title: String(q.title ?? ''),
            matter: q.matter ?? null,
            enqueuedAt: Number(q.enqueuedAt ?? 0),
          }))
        : [],
      discovered: normalizeDiscovered(parsed.discovered),
    }
  }

  savePollerState(state) {
    writeJsonAtomic(this.pollerFile, state)
  }

  loadMeta() {
    return { ...defaultMeta(), ...readJson(this.metaFile, defaultMeta()) }
  }

  saveMeta(data) {
    writeJsonAtomic(this.metaFile, { ...defaultMeta(), ...(data || {}) })
  }

  /** 写入入站任务 inbox，返回 cursor */
  appendInbox(entry) {
    ensureDir(this.stateDir)
    const meta = this.loadMeta()
    meta.lastPollAt = Date.now()
    meta.cursor = (meta.cursor || 0) + 1
    this.saveMeta(meta)
    const row = { ...entry, cursor: meta.cursor }
    appendFileSync(this.inboxFile, `${JSON.stringify(row)}\n`, {
      encoding: 'utf8',
      mode: 0o600,
      flag: 'a',
    })
    return meta.cursor
  }

  listInbox(limit = 20, unreadOnly = false, afterCursor = 0) {
    let rows = []
    try {
      rows = readFileSync(this.inboxFile, 'utf8')
        .trim()
        .split(/\n+/)
        .filter(Boolean)
        .map(line => JSON.parse(line))
    } catch {
      return []
    }
    if (afterCursor) {
      rows = rows.filter(x => Number(x.cursor || 0) > Number(afterCursor))
    }
    if (unreadOnly) {
      rows = rows.filter(x => !x.readAt)
    }
    return rows.slice(-limit)
  }

  markInboxRead(uptoCursor = Infinity) {
    let rows = []
    try {
      rows = readFileSync(this.inboxFile, 'utf8')
        .trim()
        .split(/\n+/)
        .filter(Boolean)
        .map(line => JSON.parse(line))
    } catch {
      return 0
    }
    let changed = 0
    const now = Date.now()
    rows = rows.map(row => {
      if (!row.readAt && Number(row.cursor || 0) <= uptoCursor) {
        changed += 1
        return { ...row, readAt: now }
      }
      return row
    })
    writeFileSync(
      this.inboxFile,
      rows.map(x => JSON.stringify(x)).join('\n') + (rows.length ? '\n' : ''),
      'utf8',
    )
    return changed
  }
}
