from __future__ import annotations

import hashlib
import json
import re
import shutil
from datetime import datetime, timezone
from pathlib import Path
from typing import Any

from core_db import SOURCE_ROOT, connect
from core_schema import init_core_schema
from secret_store import configured as secret_configured

TRACKED_DOCUMENTS = [
    {"slug":"TR_TS_004_2011","code":"ТР ТС 004/2011","query":"ТР ТС 004/2011 О безопасности низковольтного оборудования"},
    {"slug":"TR_TS_005_2011","code":"ТР ТС 005/2011","query":"ТР ТС 005/2011 О безопасности упаковки"},
    {"slug":"TR_TS_010_2011","code":"ТР ТС 010/2011","query":"ТР ТС 010/2011 О безопасности машин и оборудования"},
    {"slug":"TR_TS_012_2011","code":"ТР ТС 012/2011","query":"ТР ТС 012/2011 взрывоопасных средах"},
    {"slug":"TR_TS_016_2011","code":"ТР ТС 016/2011","query":"ТР ТС 016/2011 аппаратов работающих на газообразном топливе"},
    {"slug":"TR_TS_020_2011","code":"ТР ТС 020/2011","query":"ТР ТС 020/2011 электромагнитная совместимость технических средств"},
    {"slug":"TR_TS_032_2013","code":"ТР ТС 032/2013","query":"ТР ТС 032/2013 оборудование под избыточным давлением"},
    {"slug":"TR_EAEU_037_2016","code":"ТР ЕАЭС 037/2016","query":"ТР ЕАЭС 037/2016 ограничение опасных веществ"},
    {"slug":"TR_EAEU_043_2017","code":"ТР ЕАЭС 043/2017","query":"ТР ЕАЭС 043/2017 средства пожарной безопасности"},
    {"slug":"TR_TS_017_2011","code":"ТР ТС 017/2011","query":"ТР ТС 017/2011 продукция легкой промышленности"},
    {"slug":"RF_PP_2425","code":"ПП РФ № 2425","query":"Постановление Правительства РФ 2425 обязательная сертификация декларирование"},
]

def utc_now() -> str:
    return datetime.now(timezone.utc).replace(microsecond=0).isoformat()

def safe_slug(value: str) -> str:
    return re.sub(r"[^A-Za-z0-9_.-]+", "_", value).strip("_") or "document"

def normalize_text(text: str) -> str:
    return re.sub(r"\s+", " ", text.replace("\xa0", " ")).strip()

def content_hash(text: str) -> str:
    return hashlib.sha256(normalize_text(text).encode("utf-8")).hexdigest()

def init_source_schema() -> None:
    init_core_schema()
    SOURCE_ROOT.mkdir(parents=True, exist_ok=True)
    with connect() as con:
        for item in TRACKED_DOCUMENTS:
            con.execute("""
                INSERT INTO tracked_documents(
                    source_key,document_slug,document_code,search_query,enabled
                ) VALUES('techexpert',?,?,?,1)
                ON CONFLICT(source_key,document_slug) DO UPDATE SET
                  document_code=excluded.document_code,
                  search_query=excluded.search_query
            """,(item["slug"],item["code"],item["query"]))
        if secret_configured("techexpert"):
            con.execute("UPDATE connectors SET status=CASE WHEN status='not_configured' THEN 'configured' ELSE status END WHERE connector_key='techexpert'")
        con.commit()

def source_status() -> dict[str, Any]:
    init_source_schema()
    with connect() as con:
        source=con.execute("""
            SELECT connector_key AS source_key, connector_name AS source_name,
                   base_url,status,last_test_at AS last_login_at,last_sync_at,last_error,
                   meta_json
            FROM connectors WHERE connector_key='techexpert'
        """).fetchone()
        docs=con.execute("""
            SELECT document_slug,document_code,discovered_url,last_checked_at,
                   last_changed_at,last_error,
                   (SELECT COUNT(*) FROM source_snapshots s
                    WHERE s.source_key='techexpert'
                      AND s.document_slug=d.document_slug) AS versions
            FROM tracked_documents d
            WHERE source_key='techexpert' AND enabled=1
            ORDER BY document_code
        """).fetchall()
        count=con.execute("SELECT COUNT(*) FROM source_snapshots WHERE source_key='techexpert'").fetchone()[0]
    return {
        "source": dict(source) if source else None,
        "documents":[dict(x) for x in docs],
        "configured": secret_configured("techexpert"),
        "snapshots_total": int(count),
    }

def _snapshot_folder(document_slug: str, checked_at: str) -> Path:
    stamp=re.sub(r"[^0-9]","",checked_at)[:14]
    folder=SOURCE_ROOT/"techexpert"/safe_slug(document_slug)/stamp
    folder.mkdir(parents=True,exist_ok=True)
    return folder

def save_snapshot(*,document_slug:str,document_code:str,source_url:str,title:str,text:str,html:str,
                  downloaded_name:str|None=None,downloaded_bytes:bytes|None=None) -> dict[str,Any]:
    init_source_schema()
    checked_at=utc_now()
    digest=content_hash(text)
    folder=_snapshot_folder(document_slug,checked_at)
    text_path=folder/"document.txt"
    html_path=folder/"document.html"
    manifest_path=folder/"manifest.json"
    text_path.write_text(text,encoding="utf-8")
    html_path.write_text(html,encoding="utf-8")
    downloaded_path=None
    if downloaded_name and downloaded_bytes:
        downloaded_path=folder/safe_slug(downloaded_name)
        downloaded_path.write_bytes(downloaded_bytes)

    with connect() as con:
        previous=con.execute("""
            SELECT last_hash,last_snapshot_id FROM tracked_documents
            WHERE source_key='techexpert' AND document_slug=?
        """,(document_slug,)).fetchone()
        changed=not previous or previous["last_hash"]!=digest
        if not changed:
            con.execute("""
                UPDATE tracked_documents SET discovered_url=?,last_checked_at=?,last_error=NULL
                WHERE source_key='techexpert' AND document_slug=?
            """,(source_url,checked_at,document_slug))
            con.commit()
            shutil.rmtree(folder,ignore_errors=True)
            return {"changed":False,"hash":digest,"checked_at":checked_at}

        con.execute("UPDATE source_snapshots SET is_current=0 WHERE source_key='techexpert' AND document_slug=?",(document_slug,))
        manifest={
            "source":"Техэксперт","document_slug":document_slug,"document_code":document_code,
            "source_url":source_url,"title":title,"checked_at":checked_at,
            "content_hash":digest,"downloaded_file":downloaded_path.name if downloaded_path else None,
            "raw_html_is_authoritative":True,
            "text_extraction_note":"TXT является производным представлением. Исходный HTML хранится полностью.",
        }
        manifest_path.write_text(json.dumps(manifest,ensure_ascii=False,indent=2),encoding="utf-8")
        cur=con.execute("""
            INSERT INTO source_snapshots(
              source_key,document_slug,document_code,source_url,checked_at,content_hash,title,
              text_path,html_path,downloaded_file_path,manifest_path,is_current,extraction_status,extraction_meta_json
            ) VALUES('techexpert',?,?,?,?,?,?,?,?,?,?,1,'needs_review',?)
        """,(document_slug,document_code,source_url,checked_at,digest,title,str(text_path),str(html_path),
             str(downloaded_path) if downloaded_path else None,str(manifest_path),
             json.dumps({"raw_html_authoritative":True},ensure_ascii=False)))
        con.execute("""
            UPDATE tracked_documents SET discovered_url=?,last_checked_at=?,last_changed_at=?,
              last_hash=?,last_snapshot_id=?,last_error=NULL
            WHERE source_key='techexpert' AND document_slug=?
        """,(source_url,checked_at,checked_at,digest,cur.lastrowid,document_slug))
        con.commit()

    current_dir=SOURCE_ROOT/"techexpert"/safe_slug(document_slug)/"current"
    if current_dir.exists(): shutil.rmtree(current_dir)
    current_dir.mkdir(parents=True,exist_ok=True)
    for src in folder.iterdir():
        if src.is_file(): shutil.copy2(src,current_dir/src.name)
    return {"changed":True,"hash":digest,"checked_at":checked_at,"folder":str(folder)}

def set_source_error(document_slug:str,error:str)->None:
    init_source_schema(); now=utc_now()
    with connect() as con:
        con.execute("""UPDATE tracked_documents SET last_checked_at=?,last_error=?
                       WHERE source_key='techexpert' AND document_slug=?""",(now,error[:2000],document_slug))
        con.commit()

def set_source_sync_result(*,error:str|None=None)->None:
    init_source_schema(); now=utc_now()
    with connect() as con:
        con.execute("""UPDATE connectors SET last_sync_at=?,status=?,last_error=?
                       WHERE connector_key='techexpert'""",
                    (now,"error" if error else "ok",error[:2000] if error else None))
        con.commit()

def set_login_ok()->None:
    init_source_schema(); now=utc_now()
    with connect() as con:
        con.execute("""UPDATE connectors SET last_test_at=?,status='ok',last_error=NULL
                       WHERE connector_key='techexpert'""",(now,))
        con.commit()

def list_snapshots(document_slug:str|None=None)->list[dict[str,Any]]:
    init_source_schema()
    sql="""
        SELECT s.*,
               kd.status AS knowledge_status,
               kd.completeness_status,
               kd.completeness_score,
               (
                 SELECT COUNT(*)
                 FROM knowledge_rules kr
                 WHERE kr.knowledge_document_id=kd.id
               ) AS knowledge_rules_count
        FROM source_snapshots s
        LEFT JOIN knowledge_documents kd
          ON kd.source_snapshot_id=s.id
    """
    params=[]
    where=["s.source_key='techexpert'"]
    if document_slug:
        where.append("s.document_slug=?")
        params.append(document_slug)
    sql+=" WHERE "+" AND ".join(where)
    sql+=" ORDER BY s.checked_at DESC,s.id DESC"
    with connect() as con:
        return [dict(x) for x in con.execute(sql,params).fetchall()]

def get_snapshot(snapshot_id:int)->dict[str,Any]|None:
    init_source_schema()
    with connect() as con:
        row=con.execute("SELECT * FROM source_snapshots WHERE id=?",(snapshot_id,)).fetchone()
        return dict(row) if row else None

def delete_snapshot(snapshot_id:int)->dict[str,Any]:
    snap=get_snapshot(snapshot_id)
    if not snap: raise RuntimeError("Версия не найдена.")
    folder=Path(snap.get("manifest_path") or "").parent
    with connect() as con:
        con.execute("DELETE FROM source_snapshots WHERE id=?",(snapshot_id,))
        remaining=con.execute("""
            SELECT id,content_hash,checked_at,source_url FROM source_snapshots
            WHERE source_key=? AND document_slug=? ORDER BY checked_at DESC,id DESC LIMIT 1
        """,(snap["source_key"],snap["document_slug"])).fetchone()
        con.execute("UPDATE source_snapshots SET is_current=0 WHERE source_key=? AND document_slug=?",
                    (snap["source_key"],snap["document_slug"]))
        if remaining:
            con.execute("UPDATE source_snapshots SET is_current=1 WHERE id=?",(remaining["id"],))
            con.execute("""UPDATE tracked_documents SET last_snapshot_id=?,last_hash=?,last_checked_at=?,discovered_url=?
                           WHERE source_key=? AND document_slug=?""",
                        (remaining["id"],remaining["content_hash"],remaining["checked_at"],remaining["source_url"],
                         snap["source_key"],snap["document_slug"]))
        else:
            con.execute("""UPDATE tracked_documents SET last_snapshot_id=NULL,last_hash=NULL,last_changed_at=NULL
                           WHERE source_key=? AND document_slug=?""",(snap["source_key"],snap["document_slug"]))
        con.commit()
    if folder.exists() and SOURCE_ROOT.resolve() in folder.resolve().parents:
        shutil.rmtree(folder,ignore_errors=True)
    return {"deleted":snapshot_id,"document_slug":snap["document_slug"]}
