"""
gs_sync_2way.py — Google Sheets 2-Way Sync
-------------------------------------------
Cấu trúc Google Drive:
  📁 {MST}/
    📁 doanhthu/
      📁 thu/
        📁 2026/
          📄 thu-Thang-01  (Google Sheet)
      📁 chi/
        📁 2026/
          📄 chi-Thang-01  (Google Sheet)

IDs thư mục/file lưu trong SQLite (bảng gs_sync_meta).
"""
import os
import logging
from datetime import datetime

SCOPES = [
    "https://www.googleapis.com/auth/drive.file",
]

_SRC_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
TOKEN_PATH = os.path.join(_SRC_DIR, "token.json")
CLIENT_SECRET_PATH = os.path.join(
    _SRC_DIR,
    "client_secret_936373908845-dqka7hubi5ndqvp78f3f2h6rk2dfjpad"
    ".apps.googleusercontent.com.json"
)

try:
    import gspread
    from google.oauth2.credentials import Credentials
    from google.auth.transport.requests import Request
    from google_auth_oauthlib.flow import InstalledAppFlow
    from googleapiclient.discovery import build as drive_build
    _HAS_GSPREAD = True
except ImportError:
    _HAS_GSPREAD = False


def is_ready() -> bool:
    return _HAS_GSPREAD


# ─── Auth ─────────────────────────────────────────────────────────────────────
def _delete_token():
    """Xóa token.json khi bị thu hồi."""
    try:
        if os.path.exists(TOKEN_PATH):
            os.remove(TOKEN_PATH)
    except Exception:
        pass

def _run_oauth_flow():
    """Mở browser để đăng nhập và lấy token mới."""
    if not os.path.exists(CLIENT_SECRET_PATH):
        return None, "Không tìm thấy client_secret.json."
    try:
        flow = InstalledAppFlow.from_client_secrets_file(CLIENT_SECRET_PATH, SCOPES)
        creds = flow.run_local_server(port=0)
        with open(TOKEN_PATH, "w", encoding="utf-8") as f:
            f.write(creds.to_json())
        return creds, None
    except Exception as e:
        return None, f"Lỗi xác thực OAuth: {e}"

def _get_creds():
    creds = None

    # Đọc token đã lưu
    if os.path.exists(TOKEN_PATH):
        try:
            creds = Credentials.from_authorized_user_file(TOKEN_PATH, SCOPES)
        except Exception:
            creds = None

    # Refresh nếu hết hạn
    if creds and creds.expired and creds.refresh_token:
        try:
            creds.refresh(Request())
            with open(TOKEN_PATH, "w", encoding="utf-8") as f:
                f.write(creds.to_json())
        except Exception as e:
            # invalid_grant = token bị thu hồi → cần auth lại
            logging.warning(f"Refresh token thất bại ({e}), sẽ re-auth.")
            _delete_token()
            creds = None

    # Nếu chưa có hoặc không valid → OAuth flow
    if not creds or not creds.valid:
        creds, err = _run_oauth_flow()
        if err:
            return None, err

    return creds, None


def _get_clients(retry: bool = True):
    """Trả về (gspread_client, drive_service, error)."""
    creds, err = _get_creds()
    if err:
        return None, None, err
    try:
        gc = gspread.authorize(creds)
        drive = drive_build("drive", "v3", credentials=creds)
        # Kiểm tra token
        drive.about().get(fields="user").execute()
        return gc, drive, None
    except Exception as e:
        err_str = str(e).lower()
        if retry and ("invalid_grant" in err_str or "expired" in err_str or "revoked" in err_str):
            logging.warning("Token revoked/expired during client creation, retrying auth...")
            _delete_token()
            return _get_clients(retry=False)
        return None, None, str(e)


# ─── Drive folder helpers ──────────────────────────────────────────────────────
def _create_drive_folder(drive, name: str, parent_id: str = None) -> str:
    """Tạo thư mục trên Drive, trả về folder ID."""
    body = {
        "name": name,
        "mimeType": "application/vnd.google-apps.folder",
    }
    if parent_id:
        body["parents"] = [parent_id]
    folder = drive.files().create(body=body, fields="id").execute()
    return folder["id"]


def _get_or_create_folder(drive, name: str, parent_id: str, db_path: str, meta_key: str) -> tuple:
    """
    Lấy folder ID từ SQLite, nếu chưa có thì tạo mới trên Drive và lưu lại.
    Returns: (folder_id, error)
    """
    from core.revenue_db import get_gs_meta, set_gs_meta

    folder_id = get_gs_meta(meta_key, db_path)
    if folder_id:
        # Kiểm tra folder còn tồn tại không
        try:
            drive.files().get(fileId=folder_id, fields="id").execute()
            return folder_id, None
        except Exception:
            folder_id = None  # Folder đã xoá, tạo lại

    try:
        folder_id = _create_drive_folder(drive, name, parent_id)
        set_gs_meta(meta_key, folder_id, db_path)
        return folder_id, None
    except Exception as e:
        return None, f"Không tạo được thư mục '{name}': {e}"


def _get_or_create_sheet(gc, drive, name: str, folder_id: str, db_path: str, meta_key: str) -> tuple:
    """
    Lấy Google Sheet ID từ SQLite, nếu chưa có thì tạo mới trong folder và lưu lại.
    Returns: (spreadsheet, sheet_id, error)
    """
    from core.revenue_db import get_gs_meta, set_gs_meta

    sheet_id = get_gs_meta(meta_key, db_path)
    if sheet_id:
        try:
            sh = gc.open_by_key(sheet_id)
            return sh, sheet_id, None
        except Exception:
            sheet_id = None  # File đã xoá, tạo lại

    try:
        sh = gc.create(name, folder_id=folder_id)
        sheet_id = sh.id
        set_gs_meta(meta_key, sheet_id, db_path)

        # Xoá sheet mặc định "Sheet1" nếu muốn
        # (giữ lại để tránh lỗi khi chưa có tab nào khác)
        return sh, sheet_id, None
    except Exception as e:
        return None, None, f"Không tạo được file '{name}': {e}"


# ─── Main sync ────────────────────────────────────────────────────────────────
def sync_2way(local_entries: list, year: int, month_idx: int, db_path: str, folder_name: str = "Thu-Chi", retry_sync: bool = True) -> dict:
    """
    Đồng bộ 2 chiều.
    folder_name: "Thu" hoặc "Chi" để tách thư mục trên Drive.
    """
    if not _HAS_GSPREAD:
        return {"success": False, "message": "Chưa cài gspread / google-api-client."}

    gc, drive, err = _get_clients()
    if err:
        return {"success": False, "message": err}

    try:
        from core.drive_sync import get_drive_parent_name
        parent_name = get_drive_parent_name()
        
        # 1. Tạo / lấy thư mục cha (MST rễ)
        inihd_id, err = _get_or_create_folder(drive, parent_name, None, db_path, "folder_INIHD")
        if err:
            return {"success": False, "message": err}

        # 2. Tạo / lấy thư mục doanhthu trong (MST rễ)
        kd_id, err = _get_or_create_folder(drive, "doanhthu", inihd_id, db_path, "folder_doanhthu")
        if err:
            return {"success": False, "message": err}

        # 3. Tạo / lấy thư mục con (thu hoặc chi) trong doanhthu
        folder_name_lower = folder_name.lower()  # "Thu"→"thu", "Chi"→"chi"
        parent_folder_id, err = _get_or_create_folder(drive, folder_name_lower, kd_id, db_path, f"folder_{folder_name_lower}")
        if err:
            return {"success": False, "message": err}

        # 3. Tạo / lấy thư mục năm (trong thu hoặc chi)
        year_id, err = _get_or_create_folder(drive, str(year), parent_folder_id, db_path, f"folder_{folder_name_lower}_{year}")
        if err:
            return {"success": False, "message": err}

        # 4. Tạo / lấy file Google Sheet cho tháng
        if month_idx == 0:
            sheet_name = f"{folder_name_lower}-tong-hop"
            meta_key   = f"sheet_{folder_name_lower}_{year}_tong-hop"
        else:
            sheet_name = f"{folder_name_lower}-thang-{month_idx:02d}"
            meta_key   = f"sheet_{folder_name_lower}_{year}_{month_idx:02d}"

        sh, sheet_id, err = _get_or_create_sheet(gc, drive, sheet_name, year_id, db_path, meta_key)
        if err:
            return {"success": False, "message": err}

        sheet_url = f"https://docs.google.com/spreadsheets/d/{sheet_id}"

        # 5. Tìm hoặc tạo worksheet đầu tiên
        worksheets = sh.worksheets()
        if worksheets:
            worksheet = worksheets[0]
            # Đổi tên nếu cần (tránh "Sheet1")
            if worksheet.title in ("Sheet1", "Trang tính 1"):
                try:
                    worksheet.update_title("Data")
                except Exception:
                    pass
        else:
            worksheet = sh.add_worksheet(title="Data", rows="2000", cols="6")

        # Đảm bảo có header
        header = worksheet.row_values(1)
        if not header or header[0] != "ID":
            # [ID, Ngày, Số tiền, Loại, Mã SP, Số lượng, Ghi chú, Thu/Chi]
            worksheet.update("A1:H1", [["ID", "Ngày", "Số tiền (VNĐ)", "Loại", "Mã SP", "Số lượng", "Ghi chú", "Thu/Chi"]])
            worksheet.format("A1:H1", {
                "textFormat": {"bold": True, "foregroundColor": {"red": 1, "green": 1, "blue": 1}},
                "backgroundColor": {"red": 0.1, "green": 0.25, "blue": 0.68}
            })

        # 6. Đọc dữ liệu từ Sheet
        try:
            sheet_records = worksheet.get_all_records(
                expected_headers=["ID", "Ngày", "Số tiền (VNĐ)", "Loại", "Mã SP", "Số lượng", "Ghi chú", "Thu/Chi"]
            )
        except Exception:
            sheet_records = worksheet.get_all_records()

        sheet_map = {str(r.get("ID", "")): r for r in sheet_records if r.get("ID")}
        local_map = {str(e["id"]): e for e in local_entries}

        to_push = []
        to_pull = []

        # Local → Push
        for loc_id, d in local_map.items():
            if loc_id not in sheet_map:
                try:
                    d_str = datetime.strptime(d["entry_date"], "%Y-%m-%d").strftime("%d/%m/%Y")
                except Exception:
                    d_str = d["entry_date"]
                amount = d["amount"]
                sign_label = folder_name if folder_name in ("Thu", "Chi") else ("Thu" if amount >= 0 else "Chi")
                to_push.append([
                    loc_id, d_str, abs(amount),
                    d.get("category_label", d.get("category", "")),
                    d.get("product_code", ""),
                    d.get("quantity", 0),
                    d.get("note", "").replace("\n", " "),
                    sign_label,
                ])

        # Sheet → Pull
        for sh_id, sd in sheet_map.items():
            if sh_id and sh_id not in local_map:
                try:
                    d_str = str(sd.get("Ngày", ""))
                    if "/" in d_str:
                        p = d_str.split("/")
                        entry_date = f"{p[2]}-{p[1].zfill(2)}-{p[0].zfill(2)}" if len(p) == 3 else datetime.now().strftime("%Y-%m-%d")
                    else:
                        entry_date = d_str or datetime.now().strftime("%Y-%m-%d")

                    amt_raw = sd.get("Số tiền (VNĐ)", 0)
                    if isinstance(amt_raw, str):
                        amt_raw = amt_raw.replace(",", "").replace(".", "").strip()
                    amount_val = int(float(amt_raw)) if amt_raw else 0

                    sign = str(sd.get("Thu/Chi", "Thu")).strip().lower()
                    if sign in ("chi", "expense"):
                        amount_val = -abs(amount_val)

                    cat_raw = str(sd.get("Loại", "")).lower()
                    if "bán" in cat_raw:
                        cat = "thu_ban_hang"
                    elif "dịch vụ" in cat_raw:
                        cat = "thu_dich_vu"
                    elif "chi" in cat_raw:
                        cat = "chi_nhap_hang"
                    else:
                        cat = "thu_ban_hang"

                    # Đọc Mã SP và Số lượng
                    p_code = str(sd.get("Mã SP", "")).strip()
                    p_qty_raw = sd.get("Số lượng", 0)
                    try:
                        p_qty = float(p_qty_raw) if p_qty_raw else 0.0
                    except:
                        p_qty = 0.0

                    if amount_val != 0:
                        to_pull.append({
                            "id": sh_id, "entry_date": entry_date,
                            "amount": amount_val, "category": cat,
                            "note": str(sd.get("Ghi chú", "")),
                            "product_code": p_code if p_code else None,
                            "quantity": p_qty
                        })
                except Exception as ex:
                    logging.warning(f"Bỏ qua dòng lỗi: {sd} — {ex}")

        if to_push:
            worksheet.append_rows(to_push, value_input_option="USER_ENTERED")

        return {
            "success": True,
            "message": f"✅ Đồng bộ hoàn tất!\n⬆️ Đẩy lên: {len(to_push)} dòng\n⬇️ Tải về: {len(to_pull)} dòng",
            "pushed": len(to_push),
            "pulled": len(to_pull),
            "new_entries": to_pull,
            "sheet_url": sheet_url,
        }

    except Exception as e:
        err_str = str(e).lower()
        if retry_sync and ("invalid_grant" in err_str or "expired" in err_str or "revoked" in err_str):
            logging.warning("Invalid grant detected during sync process, re-authenticating and retrying sync...")
            _delete_token()
            return sync_2way(local_entries, year, month_idx, db_path, folder_name, retry_sync=False)
        return {"success": False, "message": f"Lỗi đồng bộ: {e}"}
