# ============================================================
# core/backup.py
# Tự động backup file .db theo ngày
# ============================================================

import os
import shutil
import zipfile
import glob
from datetime import datetime
from typing import Optional
from core.company_manager import get_active_db_path

import os
import shutil
import zipfile
import glob
from datetime import datetime
from typing import Optional
from core.company_manager import get_active_db_path
from core.utils import get_company_root_path

def _get_backup_dir() -> str:
    """
    Trả về thư mục backup riêng cho từng MST công ty: <MST>/database/backups/
    """
    folder = get_company_root_path(os.path.join("database", "backups"))
    os.makedirs(folder, exist_ok=True)
    return folder


def backup_company(company_code: str = "", note: str = "", db_path: Optional[str] = None, output_dir: Optional[str] = None) -> Optional[str]:
    """
    Backup DB → <MST>/database/backups/Backup_[MST]_[YYYYMMDD_HHMMSS].zip
    """
    if not db_path:
        db_path = get_active_db_path()
    if not db_path or not os.path.exists(db_path):
        print(f"❌ Không tìm thấy DB hoạt động!")
        return None

    from core.utils import get_company_tax_id
    mst = get_company_tax_id() or "unknown"

    backup_dir = _get_backup_dir()
    target_dir = output_dir or backup_dir
    os.makedirs(target_dir, exist_ok=True)
    ts = datetime.now().strftime("%Y%m%d_%H%M%S")
    dest_dir = os.path.join(backup_dir, ts)
    os.makedirs(dest_dir, exist_ok=True)

    db_name = os.path.basename(db_path)
    shutil.copy2(db_path, os.path.join(dest_dir, db_name))

    # Backup contracts DB cùng MST nếu tồn tại.
    contracts_db_path = get_company_root_path(os.path.join("contracts", "contracts.db"))
    if os.path.exists(contracts_db_path):
        shutil.copy2(contracts_db_path, os.path.join(dest_dir, "contracts.db"))

    if note:
        with open(os.path.join(dest_dir, "note.txt"), "w", encoding="utf-8") as f:
            f.write(f"{mst} - {ts}\n{note}\n")

    zip_path = os.path.join(target_dir, f"Backup_{mst}_{ts}.zip")
    with zipfile.ZipFile(zip_path, "w", zipfile.ZIP_DEFLATED) as zf:
        for fn in os.listdir(dest_dir):
            zf.write(os.path.join(dest_dir, fn), fn)
    shutil.rmtree(dest_dir)

    size_kb = os.path.getsize(zip_path) / 1024
    print(f"✅ Backup dữ liệu MST {mst}: {os.path.basename(zip_path)} ({size_kb:.1f} KB)")

    if os.path.abspath(target_dir) == os.path.abspath(backup_dir):
        _prune_old_backups(backup_dir)
    return zip_path


def backup_all(note: str = "", db_path: Optional[str] = None, output_dir: Optional[str] = None) -> list:
    r = backup_company(note=note, db_path=db_path, output_dir=output_dir)
    if r:
        return [r]
    return []


def list_backups(company_code: str = "") -> list:
    backup_dir = _get_backup_dir()
    pattern = os.path.join(backup_dir, "Backup_*.zip")
    files = sorted(glob.glob(pattern), reverse=True)
    result = []
    for f in files:
        stat = os.stat(f)
        result.append({
            "path":     f,
            "name":     os.path.basename(f),
            "size_kb":  round(stat.st_size / 1024, 1),
            "created":  datetime.fromtimestamp(stat.st_ctime).strftime("%Y-%m-%d %H:%M"),
        })
    return result


def restore_backup(company_code: str, zip_path: str, confirm: bool = False) -> bool:
    if not confirm:
        ans = input(f"⚠️ XÁC NHẬN khôi phục DB từ {os.path.basename(zip_path)}? [yes/N]: ")
        if ans.strip().lower() != "yes":
            print("Đã hủy.")
            return False

    db_path = get_active_db_path()

    backup_company(note="Auto-backup trước khi restore")

    backup_dir = _get_backup_dir()
    tmp_dir = os.path.join(backup_dir, "_restore_tmp")
    os.makedirs(tmp_dir, exist_ok=True)
    with zipfile.ZipFile(zip_path, "r") as zf:
        zf.extractall(tmp_dir)

    db_files = [f for f in os.listdir(tmp_dir) if f.endswith(".db")]
    if not db_files:
        print("❌ Không tìm thấy file .db trong backup")
        return False

    # Khôi phục DB kế toán chính theo đúng tên file active DB.
    active_db_name = os.path.basename(db_path)
    active_db_zip_path = os.path.join(tmp_dir, active_db_name)
    if os.path.exists(active_db_zip_path):
        shutil.copy2(active_db_zip_path, db_path)
    else:
        # Fallback: dùng file .db đầu tiên khác contracts.db để giữ tương thích backup cũ.
        fallback_candidates = [f for f in db_files if f.lower() != "contracts.db"]
        if fallback_candidates:
            shutil.copy2(os.path.join(tmp_dir, fallback_candidates[0]), db_path)
        else:
            print("❌ Không tìm thấy DB kế toán chính trong backup")
            shutil.rmtree(tmp_dir)
            return False

    # Khôi phục contracts.db nếu có trong gói backup.
    contracts_zip_path = os.path.join(tmp_dir, "contracts.db")
    if os.path.exists(contracts_zip_path):
        contracts_dst = get_company_root_path(os.path.join("contracts", "contracts.db"))
        os.makedirs(os.path.dirname(contracts_dst), exist_ok=True)
        shutil.copy2(contracts_zip_path, contracts_dst)

    shutil.rmtree(tmp_dir)
    print(f"✅ Đã khôi phục dữ liệu từ {os.path.basename(zip_path)}")
    return True


KEEP_DAYS = 30

def _prune_old_backups(backup_dir: str = "") -> None:
    if not backup_dir:
        backup_dir = _get_backup_dir()
    pattern = os.path.join(backup_dir, "Backup_*.zip")
    files = sorted(glob.glob(pattern), reverse=True)
    for old in files[KEEP_DAYS:]:
        os.remove(old)


