/**
 * get_annexes Tool - 별표/서식 조회 + 텍스트 추출
 */

import { z } from "zod"
import type { LawApiClient } from "../lib/api-client.js"
import { fetchWithRetry } from "../lib/fetch-with-retry.js"
import { parseAnnexFile } from "../lib/annex-file-parser.js"
import { truncateResponse, MAX_RESPONSE_SIZE } from "../lib/schemas.js"
import { formatToolError, notFoundResponse } from "../lib/errors.js"
import { getLawSiteBaseUrl } from "../lib/law-url-config.js"

/** 법제처 별표/서식 API 응답 개별 항목 */
interface AnnexItem {
  별표번호?: string
  별표명?: string
  별표종류?: string
  별표서식파일링크?: string
  별표서식PDF파일링크?: string
  별표파일링크?: string
  관련법령명?: string
  관련자치법규명?: string
  관련행정규칙명?: string
  자치법규시행일자?: string
  공포일자?: string
  소관부처?: string
  소관부처명?: string
  지자체기관명?: string
}

const LAW_BASE_URL = getLawSiteBaseUrl()

export const GetAnnexesSchema = z.object({
  lawName: z.string().describe("법령명 (예: '관세법'). 별표를 바로 지정하려면 '... 별표4' 또는 '... 별표1의2'처럼 함께 입력 가능"),
  knd: z.enum(["1", "2", "3", "4", "5"]).optional().describe("1=별표, 2=서식, 3=부칙별표, 4=부칙서식, 5=전체"),
  bylSeq: z.string().optional().describe("별표번호 (예: '000300'). 지정 시 해당 별표 파일을 다운로드하여 텍스트로 추출"),
  annexNo: z.string().optional().describe("별표 번호 (예: '4', '별표4', '제4호'). bylSeq 대체 입력"),
  apiKey: z.string().optional().describe("법제처 Open API 인증키(OC). 사용자가 제공한 경우 전달")
})

export type GetAnnexesInput = z.infer<typeof GetAnnexesSchema>

export async function getAnnexes(
  apiClient: LawApiClient,
  input: GetAnnexesInput
): Promise<{ content: Array<{ type: string, text: string }>, isError?: boolean }> {
  try {
    const parsedLawInput = parseLawNameAndHint(input.lawName)
    const normalizedLawName = parsedLawInput.normalizedLawName || input.lawName
    const annexSelector = (input.bylSeq || input.annexNo || parsedLawInput.annexNo || "").trim()

    let annexList: AnnexItem[] = []
    let lawType: string = "law"

    // 법제처 API는 결과 1건일 때 배열 대신 단일 객체를 반환하므로 정규화
    const toArray = (v: unknown): AnnexItem[] =>
      v == null ? [] : Array.isArray(v) ? v : [v]

    const parseAnnexResponse = (jsonText: string): { list: AnnexItem[], type: string } => {
      try {
        const json = JSON.parse(jsonText)
        const adminResult = json?.admRulBylSearch
        const licResult = json?.licBylSearch
        // 법제처 행정규칙 별표 응답의 배열 키는 admrulbyl (admbyl 아님). 구버전 호환 위해 admbyl도 폴백.
        if (adminResult?.admrulbyl ?? adminResult?.admbyl)
          return { list: toArray(adminResult.admrulbyl ?? adminResult.admbyl), type: "admin" }
        if (licResult?.ordinbyl) return { list: toArray(licResult.ordinbyl), type: "ordinance" }
        if (licResult?.licbyl) return { list: toArray(licResult.licbyl), type: "law" }
        return { list: [], type: "law" }
      } catch {
        // JSON 파싱 실패 (HTML 에러 페이지 등) → 빈 배열 반환하여 fallback 진행
        return { list: [], type: "law" }
      }
    }

    // 1차: 원래 법령명 + knd 필터
    const result1 = parseAnnexResponse(await apiClient.getAnnexes({
      lawName: normalizedLawName, knd: input.knd, apiKey: input.apiKey
    }))
    annexList = result1.list
    lawType = result1.type

    // 2차: 결과 없으면 knd 제거 (법제처가 "별표"를 "서식"으로 분류하는 경우)
    if (annexList.length === 0 && input.knd) {
      const result2 = parseAnnexResponse(await apiClient.getAnnexes({
        lawName: normalizedLawName, apiKey: input.apiKey
      }))
      annexList = result2.list
      lawType = result2.type
    }

    // 3차: 모법명으로 재검색 ("여권법 시행규칙" → "여권법")
    if (annexList.length === 0) {
      const parentName = extractParentLawName(normalizedLawName)
      if (parentName) {
        const result3 = parseAnnexResponse(await apiClient.getAnnexes({
          lawName: parentName, apiKey: input.apiKey
        }))
        // 원래 법령명 매칭 필터
        const filtered = result3.list.filter((a: AnnexItem) => {
          const name = String(a.관련법령명 || a.관련자치법규명 || a.관련행정규칙명 || "").replace(/<[^>]+>/g, "")
          return name === normalizedLawName
        })
        annexList = filtered.length > 0 ? filtered : result3.list
        lawType = result3.type
      }
    }

    // 4차: "규정" 타입은 licbyl과 admbyl 양쪽에 존재 가능 → admin fallback
    if (annexList.length === 0 && /규정/.test(normalizedLawName)) {
      try {
        const adminText = await apiClient.fetchApi({
          endpoint: "lawSearch.do",
          target: "admbyl",
          type: "JSON",
          extraParams: {
            query: normalizedLawName,
            search: "2",
            display: "100",
          },
          apiKey: input.apiKey,
        })
        const result4 = parseAnnexResponse(adminText)
        if (result4.list.length > 0) {
          annexList = result4.list
          lawType = "admin"
        }
      } catch {
        // admin fallback 실패 → 무시하고 진행
      }
    }

    if (annexList.length === 0) {
      return notFoundResponse(
        `"${normalizedLawName}"에 대한 별표/서식이 법제처 DB에 없습니다.`,
        [
          "법령명 오탈자 확인 (예: '관세법 시행령' vs '관세법')",
          `search_law({ query: "${normalizedLawName}" }) 로 정확한 법령명 확인`,
          "모법에 별표가 있을 수 있음 (시행규칙 대신 시행령으로 재시도)",
        ]
      )
    }

    // 최신본 우선 정렬
    annexList.sort((a: AnnexItem, b: AnnexItem) =>
      (b.자치법규시행일자 || b.공포일자 || "").localeCompare(a.자치법규시행일자 || a.공포일자 || "")
    )

    // 관련법규명 필터링: 사용자 쿼리와 가장 일치하는 조례 우선
    const filtered = filterByRelatedLawName(annexList, normalizedLawName)

    // 별표 선택값 지정 시 → 해당 별표 파일 다운로드 + 텍스트 추출
    if (annexSelector) {
      return await extractAnnexContent(filtered, annexSelector, normalizedLawName, input.knd)
    }

    // 별표 선택값 미지정 → 기존 목록 반환
    return formatAnnexList(filtered, lawType, input, normalizedLawName)
  } catch (error) {
    return formatToolError(error, "get_annexes")
  }
}

// ─── 별표 텍스트 추출 ─────────────────────────────────

async function extractAnnexContent(
  annexList: AnnexItem[],
  annexSelector: string,
  normalizedLawName: string,
  knd?: string
): Promise<{ content: Array<{ type: string, text: string }>, isError?: boolean }> {
  // bylSeq / annexNo / lawName 내 힌트로 유연 매칭 (별표/서식 구분 위해 knd 전달)
  const matched = findMatchingAnnex(annexList, annexSelector, knd)
  if (!matched) {
    const availableBylSeq = annexList.map((a) => a.별표번호).filter(Boolean).slice(0, 20).join(", ")
    return notFoundResponse(
      `별표 선택값 "${annexSelector}"에 해당하는 항목을 찾을 수 없습니다. (법령: ${normalizedLawName})`,
      [
        `사용 가능한 별표번호(일부): ${availableBylSeq || "없음"}`,
        `예: get_annexes({ lawName: "${normalizedLawName}", bylSeq: "${annexList[0]?.별표번호 || "000100"}" })`,
        `예: get_annexes({ lawName: "${normalizedLawName} 별표4" })`,
      ]
    )
  }

  const annexTitle = matched.별표명 || "제목 없음"
  const fileLink = matched.별표서식파일링크 || matched.별표서식PDF파일링크 || matched.별표파일링크 || ""

  if (!fileLink) {
    return notFoundResponse(
      `"${annexTitle}"의 파일 링크가 법제처 응답에 포함되지 않았습니다.`,
      ["법령 전체 별표 목록을 다시 조회하세요: get_annexes({ lawName: '...' })"]
    )
  }

  // 파일 다운로드
  const downloadUrl = `${LAW_BASE_URL}${fileLink}`
  const response = await fetchWithRetry(downloadUrl, { timeout: 30000 })
  if (!response.ok) {
    return {
      content: [{ type: "text", text: `파일 다운로드 실패: HTTP ${response.status}\nURL: ${downloadUrl}` }],
      isError: true
    }
  }

  const buffer = await response.arrayBuffer()
  const result = await parseAnnexFile(buffer)

  if (result.fileType === "pdf" && result.isImageBased) {
    // 이미지 기반 PDF: 텍스트 추출 불가 → 링크 안내
    const pdfLink = matched.별표서식PDF파일링크 || fileLink
    return {
      content: [{
        type: "text",
        text: `${annexTitle}\n\n이미지 기반 PDF입니다 (${result.pageCount || "?"}페이지). 텍스트 추출이 불가합니다.\n다운로드 링크: ${LAW_BASE_URL}${pdfLink}`
      }]
    }
  }

  if (!result.success || !result.markdown) {
    // 파싱 실패 시에도 PDF 링크 안내
    const fallbackLink = matched.별표서식PDF파일링크 || fileLink
    return {
      content: [{
        type: "text",
        text: `"${annexTitle}" 텍스트 추출 실패: ${result.error || "알 수 없는 오류"}\n파일 링크: ${LAW_BASE_URL}${fallbackLink}`
      }],
      isError: true
    }
  }

  // 파싱 성공 - 묶음 별표면 요청 섹션만 추출
  let markdown = result.markdown
  const selectorNumbers = extractSelectorNumbers(annexSelector)
  if (selectorNumbers.length > 0 && isBundledAnnex(annexTitle)) {
    const extracted = extractBundledSection(markdown, selectorNumbers[0])
    if (extracted) markdown = extracted
  }

  const header = `${normalizedLawName} - ${annexTitle}\n(파일 형식: ${result.fileType.toUpperCase()}${result.pageCount ? `, ${result.pageCount}페이지` : ""})\n\n`
  const fullText = header + markdown
  return {
    content: [{
      type: "text",
      text: truncateResponse(fullText, MAX_RESPONSE_SIZE)
    }]
  }
}

// ─── 목록 포맷 (기존 동작) ────────────────────────────

function formatAnnexList(
  annexList: AnnexItem[],
  lawType: string,
  input: GetAnnexesInput,
  normalizedLawName: string
): { content: Array<{ type: string, text: string }> } {
  const kndLabel = input.knd === "1" ? "별표"
                 : input.knd === "2" ? "서식"
                 : input.knd === "3" ? "부칙별표"
                 : input.knd === "4" ? "부칙서식"
                 : "별표/서식"

  let resultText = `법령명: ${normalizedLawName}\n`
  resultText += `${kndLabel} 목록 (총 ${annexList.length}건):\n\n`

  const maxItems = Math.min(annexList.length, 20)

  for (let i = 0; i < maxItems; i++) {
    const annex = annexList[i]
    const annexTitle = annex.별표명 || "제목 없음"
    const annexType = annex.별표종류 || ""
    const annexNum = annex.별표번호 || ""

    resultText += `${i + 1}. `
    if (annexNum) resultText += `[${annexNum}] `
    resultText += `${annexTitle}`
    if (annexType) resultText += ` (${annexType})`
    resultText += `\n`

    if (lawType === "ordinance") {
      const relatedLaw = annex.관련자치법규명
      const localGov = annex.지자체기관명
      if (relatedLaw) {
        resultText += `   관련법규: ${relatedLaw.replace(/<[^>]+>/g, '')}\n`
      }
      if (localGov) {
        resultText += `   지자체: ${localGov}\n`
      }
    } else if (lawType === "admin") {
      if (annex.관련행정규칙명) resultText += `   행정규칙: ${annex.관련행정규칙명}\n`
      const dept = annex.소관부처명 || annex.소관부처
      if (dept) resultText += `   소관부처: ${dept}\n`
    } else {
      if (annex.관련법령명) resultText += `   관련법령: ${annex.관련법령명}\n`
    }

    resultText += `\n`
  }

  if (annexList.length > maxItems) {
    resultText += `\n... 외 ${annexList.length - maxItems}개 항목 (생략)\n`
  }

  resultText += `\n[주의] 별표 내용을 확인하려면 이 도구(get_annexes)를 bylSeq 파라미터와 함께 다시 호출하세요.\n예: get_annexes({ lawName: "${normalizedLawName}", bylSeq: "${annexList[0]?.별표번호 || '000100'}" })`
  resultText += `\n커넥터에서 bylSeq 입력이 제한되면 lawName에 별표번호를 함께 넣어 호출할 수 있습니다.\n예: get_annexes({ lawName: "${normalizedLawName} 별표4" })`

  return { content: [{ type: "text", text: truncateResponse(resultText) }] }
}

/**
 * 모법명 추출 (시행규칙/시행령 제거)
 * "여권법 시행규칙" → "여권법", "관세법 시행령" → "관세법"
 */
function extractParentLawName(lawName: string): string | null {
  const cleaned = lawName.replace(/\s*(시행규칙|시행령)$/, '')
  return cleaned !== lawName ? cleaned : null
}

function parseLawNameAndHint(lawName: string): { normalizedLawName: string, annexNo?: string } {
  const trimmedLawName = lawName.trim()
  // "별표1", "별표 제1호", "별표 1의2"(= 별표 제1호의2) 모두 매칭. 의-번호는 별도 캡처해 법령명에 남지 않게 한다.
  const annexHintMatch = trimmedLawName.match(/\[?\s*(별표|서식)\s*(?:제)?\s*(\d{1,6})\s*(?:호)?\s*(?:의\s*(\d{1,2}))?\s*\]?/)

  if (!annexHintMatch) {
    return { normalizedLawName: trimmedLawName }
  }

  const mainNo = Number.parseInt(annexHintMatch[2], 10)
  const subNo = annexHintMatch[3] ? Number.parseInt(annexHintMatch[3], 10) : null
  const normalizedLawName = trimmedLawName
    .replace(annexHintMatch[0], " ")
    .replace(/\s+/g, " ")
    .trim()

  if (Number.isNaN(mainNo)) {
    return { normalizedLawName: normalizedLawName || trimmedLawName }
  }

  // 의-번호가 있으면 법제처 별표번호 6자리 코드(AAAABB)로 변환 (별표 1의2 → "000102").
  // 없으면 기존 동작 유지(정수 문자열 → buildSelectorCandidates가 6자리 코드 후보 생성).
  const annexNo = subNo != null
    ? String(mainNo).padStart(4, "0") + String(subNo).padStart(2, "0")
    : String(mainNo)

  return {
    normalizedLawName: normalizedLawName || trimmedLawName,
    annexNo
  }
}

/**
 * 별표 선택값으로 항목 매칭.
 *
 * 자치법규 등에서 [별표 N]과 [별지 제N호서식]이 동일 별표번호(bylSeq)를 공유하는 경우가
 * 있어, 번호만으로 find() 하면 목록 순서상 먼저 오는 항목(주로 서식)이 잘못 선택된다.
 * (예: 서울특별시 건축 조례 — [별표4] 대지안의 공지기준 / [별지 제4호서식] 공개공지 관리대장이
 *  모두 별표번호 000400을 가짐.)
 * 따라서 번호가 일치하는 후보를 모두 모은 뒤, knd(별표/서식 의도)로 별표종류를 구분해
 * 올바른 항목을 고른다. knd 미지정 시 표(별표)를 서식보다 우선한다.
 */
function findMatchingAnnex(
  annexList: AnnexItem[],
  annexSelector: string,
  knd?: string
): AnnexItem | undefined {
  const selectorCandidates = buildSelectorCandidates(annexSelector)
  const selectorNumbers = extractSelectorNumbers(annexSelector)

  // 번호/제목으로 매칭되는 후보 "전체" 수집 (find → filter)
  const matches = annexList.filter((annex: AnnexItem) => {
    const annexNum = String(annex.별표번호 || "").trim()
    const annexTitle = String(annex.별표명 || "")
    if (annexNum && selectorCandidates.has(annexNum)) {
      return true
    }
    return selectorNumbers.some((num) => titleMatchesAnnexNumber(annexTitle, num))
  })

  if (matches.length === 0) return undefined
  if (matches.length === 1) return matches[0]

  // 별표번호 충돌 → 별표종류("별표"/"서식")로 구분
  const isForm = (a: AnnexItem) => /서식/.test(String(a.별표종류 || ""))
  const isTable = (a: AnnexItem) => /별표/.test(String(a.별표종류 || ""))

  if (knd === "2" || knd === "4") {
    // 서식을 명시적으로 요청
    return matches.find(isForm) || matches[0]
  }
  if (knd === "1" || knd === "3") {
    // 별표를 명시적으로 요청
    return matches.find(isTable) || matches[0]
  }
  // knd 미지정/전체(5): 표(별표)를 서식보다 우선
  return matches.find(isTable) || matches[0]
}

function buildSelectorCandidates(selector: string): Set<string> {
  const candidates = new Set<string>()
  const trimmed = selector.trim()

  if (!trimmed) {
    return candidates
  }

  candidates.add(trimmed)

  const numMatch = trimmed.match(/(\d{1,6})/)
  if (!numMatch) {
    return candidates
  }

  const rawDigits = numMatch[1]
  const asNumber = Number.parseInt(rawDigits, 10)
  if (Number.isNaN(asNumber)) {
    return candidates
  }

  candidates.add(rawDigits)
  candidates.add(String(asNumber))

  // 법제처 별표번호는 관행적으로 000100, 000200 형식이 많아 둘 다 허용
  candidates.add(String(asNumber).padStart(6, "0"))
  if (rawDigits.length <= 3) {
    candidates.add(String(asNumber * 100).padStart(6, "0"))
  }

  return candidates
}

function extractSelectorNumbers(selector: string): string[] {
  const numbers = new Set<string>()
  const numMatch = selector.match(/(\d{1,6})/)
  if (!numMatch) {
    return []
  }

  const rawDigits = numMatch[1]
  const asNumber = Number.parseInt(rawDigits, 10)
  if (Number.isNaN(asNumber)) {
    return []
  }

  numbers.add(String(asNumber))

  if (rawDigits.length === 6 && asNumber % 100 === 0) {
    numbers.add(String(asNumber / 100))
  }

  return Array.from(numbers)
}

function titleMatchesAnnexNumber(title: string, annexNumber: string): boolean {
  const escapedNumber = escapeRegex(annexNumber)
  const patterns = [
    new RegExp(`\\[\\s*별표\\s*${escapedNumber}\\s*\\]`),
    new RegExp(`별표\\s*제?\\s*${escapedNumber}\\s*(?:호)?`),
    new RegExp(`\\[\\s*서식\\s*${escapedNumber}\\s*\\]`),
    new RegExp(`서식\\s*제?\\s*${escapedNumber}\\s*(?:호)?`)
  ]

  if (patterns.some((pattern) => pattern.test(title))) {
    return true
  }

  // 묶음 별표 범위 매칭: "[별표1~5]", "[별표 1 ~ 5]" 등
  const num = Number.parseInt(annexNumber, 10)
  if (!Number.isNaN(num)) {
    const rangePattern = /별표\s*(\d+)\s*[~\-]\s*(\d+)/g
    let match: RegExpExecArray | null
    while ((match = rangePattern.exec(title)) !== null) {
      const start = Number.parseInt(match[1], 10)
      const end = Number.parseInt(match[2], 10)
      if (num >= start && num <= end) {
        return true
      }
    }
  }

  return false
}

function escapeRegex(value: string): string {
  return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")
}

/** 묶음 별표 여부 판별: "[별표1~5]" 같은 범위 표기가 있는지 */
function isBundledAnnex(annexTitle: string): boolean {
  return /별표\s*\d+\s*[~\-]\s*\d+/.test(annexTitle)
}

/** 묶음 별표 마크다운에서 특정 별표 섹션만 추출 */
function extractBundledSection(markdown: string, targetNum: string): string | null {
  const num = parseInt(targetNum, 10)
  if (isNaN(num)) return null

  const pattern = new RegExp(
    `(##\\s*\\[별표\\s*${num}\\][\\s\\S]*?)(?=##\\s*\\[별표\\s*\\d|$)`
  )
  const match = markdown.match(pattern)
  return match ? match[1].trim() : null
}

/**
 * 관련법규명으로 annexList 필터링: 사용자 쿼리와 가장 일치하는 조례 우선
 * 여러 조례(예: "광진구의회 복무 조례" vs "광진구 복무 조례")가 혼합된 경우 분리
 */
function filterByRelatedLawName(annexList: AnnexItem[], queryName: string): AnnexItem[] {
  if (annexList.length <= 1) return annexList

  // 쿼리에서 단어 추출
  const queryWords = queryName.split(/\s+/).filter((w) => w.length > 0)
  if (queryWords.length === 0) return annexList

  // 각 항목에 관련법규명 단어 매칭 점수 부여
  const scored = annexList.map((annex: AnnexItem) => {
    const relatedName = String(annex.관련자치법규명 || annex.관련법령명 || "")
      .replace(/<[^>]+>/g, "")   // HTML 태그 제거
    const relatedWords = relatedName.split(/\s+/).filter((w) => w.length > 0)
    // 쿼리 단어가 관련법규명에 정확히 포함되는 수
    const score = queryWords.filter((qw) => relatedWords.includes(qw)).length
    return { annex, score }
  })

  const maxScore = Math.max(...scored.map((s) => s.score))
  if (maxScore === 0) return annexList

  // 최고 점수 항목만 필터 (동점 허용)
  const best = scored.filter((s) => s.score === maxScore).map((s) => s.annex)
  return best.length > 0 ? best : annexList
}
