# -*- coding: utf-8 -*-
"""资产导入 / 导出。

两种存放策略（导入时可切换）：
- 引用（link）：只记录原文件路径，不复制。库轻，但原文件移动后失效。
- 复制（copy）：文件复制到 data/assets/<series>/<type>/ 下，随库一起管理。

漫画页批量导入时按文件名排序推导 page_index，保证画集顺序正确。
"""
from __future__ import annotations

import json
import os
import re
import shutil
from dataclasses import dataclass
from pathlib import Path

from . import thumbnail as thumb_mod
from .db import Database
from .models import (
    ASSET_TYPE_META,
    EXT_TO_TYPE,
    TEXT_TYPES,
    Asset,
    AssetType,
    Chapter,
    Series,
)

ASSETS_DIR_NAME = "assets"


# ---------------------------------------------------------------------------
# 结果对象
# ---------------------------------------------------------------------------
@dataclass
class ImportReport:
    imported: int = 0
    skipped: int = 0
    failed: list[str] = None  # type: ignore[assignment]
    thumb_generated: int = 0
    series: Series | None = None

    def __post_init__(self):
        if self.failed is None:
            self.failed = []

    @property
    def ok(self) -> bool:
        return self.imported > 0


def _natural_key(name: str) -> list:
    """自然排序键：'p2.png' < 'p10.png'。漫画页导入排序用。"""
    return [int(t) if t.isdigit() else t.lower()
            for t in re.split(r"(\d+)", name)]


# ---------------------------------------------------------------------------
# 扫描
# ---------------------------------------------------------------------------
def scan_files(root: str | os.PathLike, recursive: bool = True,
               asset_type: AssetType | None = None) -> list[tuple[Path, AssetType]]:
    """扫描目录，返回 [(文件路径, 推断类型)]。

    asset_type 指定时只收该类型扩展名的文件；否则按扩展名自动推断。
    """
    root = Path(root)
    if not root.is_dir():
        return []
    pattern = "**/*" if recursive else "*"
    out: list[tuple[Path, AssetType]] = []
    for p in root.glob(pattern):
        if not p.is_file() or p.name.startswith("."):
            continue
        suffix = p.suffix.lower()
        inferred = EXT_TO_TYPE.get(suffix)
        if asset_type is not None:
            if suffix not in ASSET_TYPE_META[asset_type].exts:
                continue
            out.append((p, asset_type))
        elif inferred is not None:
            out.append((p, inferred))
    return sorted(out, key=lambda x: (x[1].value, _natural_key(x[0].name)))


def split_name_for_page_index(name: str) -> tuple[str, int | None]:
    """从文件名提取页序号：'01-page.png' -> ('01-page', 1)；'p12.png' -> ('p12', 12)。"""
    stem = Path(name).stem
    nums = re.findall(r"\d+", stem)
    idx = int(nums[-1]) if nums else None
    return stem, idx


# ---------------------------------------------------------------------------
# 复制到资产库
# ---------------------------------------------------------------------------
def copy_into_library(src: Path, series: Series, atype: AssetType, root: Path) -> Path:
    """复制文件到 data/assets/<series名>/<type>/，重名时追加序号。"""
    safe = re.sub(r"[^\w\u4e00-\u9fff\-]+", "_", series.name).strip("_") or "unsorted"
    dest_dir = root / ASSETS_DIR_NAME / safe / atype.value
    dest_dir.mkdir(parents=True, exist_ok=True)
    dest = dest_dir / src.name
    if dest.exists():
        stem, suffix = dest.stem, dest.suffix
        i = 1
        while dest.exists():
            dest = dest_dir / f"{stem}_{i}{suffix}"
            i += 1
    shutil.copy2(src, dest)
    return dest


# ---------------------------------------------------------------------------
# 导入：文件夹
# ---------------------------------------------------------------------------
def import_folder(
    db: Database,
    folder: str | os.PathLike,
    series: Series | None = None,
    series_name: str = "",
    asset_type: AssetType | None = None,
    recursive: bool = True,
    copy_files: bool = True,
    root: Path | None = None,
) -> ImportReport:
    """从文件夹批量导入。

    - series 为空且给了 series_name 时自动建系列。
    - 资产类型统一（asset_type 指定），否则按扩展名推断。
    - 同类型的文件按自然排序生成 page_index。
    """
    report = ImportReport()
    folder = Path(folder)
    if not folder.is_dir():
        report.failed.append(f"目录不存在: {folder}")
        return report

    if series is None:
        if not series_name:
            series_name = folder.name
        series = db.create_series(Series(name=series_name))
    report.series = series

    files = scan_files(folder, recursive=recursive, asset_type=asset_type)
    if not files:
        report.failed.append("未找到可识别的文件")
        return report

    lib_root = root or Path(db.db_path).parent
    counter: dict[str, int] = {}

    for src, atype in files:
        try:
            title, page_idx = split_name_for_page_index(src.name)
            if copy_files:
                dest = copy_into_library(src, series, atype, lib_root)
                file_path = str(dest)
            else:
                file_path = str(src)

            content = _read_text_content(src) if atype in TEXT_TYPES else ""

            counter[atype.value] = counter.get(atype.value, 0) + 1
            asset = Asset(
                series_id=series.id,
                type=atype,
                title=title,
                file_path=file_path,
                content=content,
                page_index=page_idx,
                sort_order=counter[atype.value],
            )
            a = db.create_asset(asset)
            if not a.is_text and thumb_mod.make_thumb(a.file_path, a.id, lib_root):
                report.thumb_generated += 1
            report.imported += 1
        except Exception as e:
            report.failed.append(f"{src.name}: {e}")

    return report


# ---------------------------------------------------------------------------
# 文本正文读取
# ---------------------------------------------------------------------------
_TEXT_EXTS = frozenset({
    ".txt", ".md", ".mdx", ".markdown", ".csv", ".json", ".log", ".srt", ".vtt", ".rtf",
})
_CONTENT_LIMIT = 2_000_000  # 单资产正文上限（2MB），防止大文件拖垮内存


def _read_text_content(p: Path) -> str:
    """读取文本类资产正文；非文本格式或读取失败返回空串。"""
    if p.suffix.lower() not in _TEXT_EXTS:
        return ""
    try:
        return p.read_text(encoding="utf-8", errors="replace")[:_CONTENT_LIMIT]
    except OSError:
        return ""


# ---------------------------------------------------------------------------
# 导入：单文件
# ---------------------------------------------------------------------------
def import_file(
    db: Database,
    file_path: str | os.PathLike,
    series_id: int | None = None,
    chapter_id: int | None = None,
    asset_type: AssetType | None = None,
    title: str | None = None,
    copy_files: bool = True,
    root: Path | None = None,
) -> Asset | None:
    p = Path(file_path)
    if not p.is_file():
        return None
    atype = asset_type or EXT_TO_TYPE.get(p.suffix.lower()) or AssetType.REFERENCE
    series = None
    if series_id is not None:
        series = db.get_series(series_id)

    lib_root = root or Path(db.db_path).parent
    if copy_files and series is not None:
        p = copy_into_library(p, series, atype, lib_root)
    elif copy_files:
        dest_dir = lib_root / ASSETS_DIR_NAME / "unsorted" / atype.value
        dest_dir.mkdir(parents=True, exist_ok=True)
        dest = dest_dir / p.name
        if dest.exists():
            i = 1
            while dest.exists():
                dest = dest_dir / f"{p.stem}_{i}{p.suffix}"
                i += 1
        shutil.copy2(p, dest)
        p = dest

    asset = Asset(
        series_id=series_id, chapter_id=chapter_id, type=atype,
        title=title or p.stem, file_path=str(p),
        content=_read_text_content(p) if atype in TEXT_TYPES else "",
    )
    a = db.create_asset(asset)
    if not a.is_text and thumb_mod.make_thumb(a.file_path, a.id, lib_root):
        pass
    return a


# ---------------------------------------------------------------------------
# 导入 / 导出：JSON 资产库
# ---------------------------------------------------------------------------
def export_library(db: Database, path: str | os.PathLike) -> str:
    return db.export_json(path)


def import_library(
    db: Database,
    json_path: str | os.PathLike,
    base_dir: str | os.PathLike | None = None,
) -> ImportReport:
    """从导出的 JSON 还原资产库。

    base_dir 用于相对路径文件的定位（导出时若是相对路径）。
    """
    report = ImportReport()
    try:
        with open(json_path, "r", encoding="utf-8") as f:
            data = json.load(f)
    except Exception as e:
        report.failed.append(f"无法读取: {e}")
        return report

    id_map_series: dict[str, int] = {}
    for s in data.get("series", []):
        ns = Series(**{k: v for k, v in s.items() if k != "id"})
        ns = db.create_series(ns)
        id_map_series[str(s["id"])] = ns.id

    id_map_chapter: dict[str, int] = {}
    for c in data.get("chapters", []):
        sid = id_map_series.get(str(c.get("series_id")), None)
        nc = Chapter(series_id=sid, volume=c.get("volume"), number=c.get("number"),
                     title=c.get("title", ""), description=c.get("description", ""))
        nc = db.create_chapter(nc)
        id_map_chapter[str(c["id"])] = nc.id

    for t in data.get("tags", []):
        db.create_tag(t.get("name", ""), t.get("color", "#4A90D9"))

    lib_root = Path(db.db_path).parent
    for a in data.get("assets", []):
        aid = a.get("id")
        sid = id_map_series.get(str(a.get("series_id"))) if a.get("series_id") else None
        cid = id_map_chapter.get(str(a.get("chapter_id"))) if a.get("chapter_id") else None
        fpath = a.get("file_path") or ""
        if fpath and base_dir and not os.path.isabs(fpath):
            fpath = str((Path(base_dir) / fpath).resolve())
        asset = Asset.from_dict({
            "series_id": sid, "chapter_id": cid, "type": a.get("type"),
            "title": a.get("title", ""), "description": a.get("description", ""),
            "file_path": fpath, "content": a.get("content", ""),
            "page_index": a.get("page_index"), "tags": a.get("tags", []),
            "sort_order": a.get("sort_order", 0),
        })
        try:
            na = db.create_asset(asset)
            report.imported += 1
            if not na.is_text:
                thumb_mod.make_thumb(na.file_path, na.id, lib_root)
        except Exception as e:
            report.failed.append(f"{a.get('title', aid)}: {e}")

    return report
