#!/usr/bin/env python3
"""
飞书表格安全写入工具 — 自动遵守 5000 格/次 API 上限

飞书 Open API 单次写入上限为 5000 格（行×列）。
超过上限的请求会静默失败（API 不报错但数据不完整），
导致旧数据残留、新数据被部分覆盖、末尾行丢失等问题。

本模块封装了安全的分批写入和清空逻辑，所有操作自动计算
批大小确保 ≤ 4400 格/批（留 12% 安全余量）。

用法:
    from feishu_sheet_utils import FeishuSheetWriter

    writer = FeishuSheetWriter(SPREADSHEET_TOKEN, token)
    writer.clear(sheet_id, start_row=3, end_row=500, cols=26)
    writer.write(sheet_id, start_row=3, rows=data, cols=26)
"""

import time
import requests

# 飞书 API 单次写入格数上限
FEISHU_CELL_LIMIT = 5000
# 安全余量系数（0.88，即实际使用 ≤ 4400 格/批）
SAFETY_FACTOR = 0.88
# 单批最大格数
SAFE_CELLS_PER_BATCH = int(FEISHU_CELL_LIMIT * SAFETY_FACTOR)  # 4400


def max_rows_per_batch(cols):
    """根据列数计算单批最大行数（确保 ≤ 4400 格）。"""
    return max(1, SAFE_CELLS_PER_BATCH // cols)


class FeishuSheetWriter:
    """飞书表格安全写入器，自动分批遵守 5000 格上限。"""

    def __init__(self, spreadsheet_token, tenant_token):
        self.spreadsheet_token = spreadsheet_token
        self.token = tenant_token
        self.base_url = "https://open.feishu.cn/open-apis/sheets/v2"

    def _put(self, sheet_id, range_str, values, retries=3):
        """单次写入，含重试。"""
        url = f"{self.base_url}/spreadsheets/{self.spreadsheet_token}/values"
        body = {"valueRange": {"range": f"{sheet_id}!{range_str}", "values": values}}
        for attempt in range(retries):
            resp = requests.put(url, headers={
                "Authorization": f"Bearer {self.token}",
                "Content-Type": "application/json"
            }, json=body, timeout=30)
            result = resp.json()
            if result.get("code") == 0:
                return True
            print(f"  Retry {attempt+1} for {range_str}: {result.get('msg','')}")
            time.sleep(1)
        print(f"  FAILED {range_str}")
        return False

    def _col_letter(self, idx):
        """0-based column index → Excel column letter(s). 0→A, 25→Z, 26→AA."""
        result = ""
        n = idx
        while n >= 0:
            result = chr(ord('A') + n % 26) + result
            n = n // 26 - 1
        return result

    def _range_str(self, start_row, end_row, cols):
        """生成范围字符串，如 A3:Z52。"""
        end_col = self._col_letter(cols - 1)
        return f"A{start_row}:{end_col}{end_row}"

    def clear(self, sheet_id, start_row, end_row, cols):
        """
        安全清空指定区域（写入空字符串）。
        自动分批，每批 ≤ 4400 格。
        """
        if end_row < start_row:
            return
        batch_rows = max_rows_per_batch(cols)
        total = end_row - start_row + 1
        print(f"  Clearing {sheet_id} rows {start_row}-{end_row} "
              f"({total} rows × {cols} cols, batch={batch_rows} rows)")

        for batch_start in range(start_row, end_row + 1, batch_rows):
            batch_end = min(batch_start + batch_rows - 1, end_row)
            n_rows = batch_end - batch_start + 1
            empty = [[""] * cols for _ in range(n_rows)]
            rng = self._range_str(batch_start, batch_end, cols)
            ok = self._put(sheet_id, rng, empty)
            if not ok:
                print(f"  Clear batch {rng} failed, continuing...")
            time.sleep(0.15)

    def write(self, sheet_id, start_row, rows, cols):
        """
        安全写入数据行。
        自动分批，每批 ≤ 4400 格。
        rows: list of list，每行长度应为 cols。
        """
        if not rows:
            return
        batch_rows = max_rows_per_batch(cols)
        total = len(rows)
        print(f"  Writing {sheet_id} {total} rows × {cols} cols "
              f"(batch={batch_rows} rows, {batch_rows * cols} cells/batch)")

        for batch_start in range(0, total, batch_rows):
            batch = rows[batch_start:batch_start + batch_rows]
            sr = start_row + batch_start
            er = sr + len(batch) - 1
            rng = self._range_str(sr, er, cols)
            ok = self._put(sheet_id, rng, batch)
            if not ok:
                print(f"  Write batch {rng} failed!")
            time.sleep(0.3)

    def clear_excess(self, sheet_id, total_written, old_count, cols):
        """清除超出新数据范围的旧行残留。"""
        if old_count <= total_written:
            return
        clear_start = start_row_base = 3  # 假设数据从第3行开始
        actual_start = clear_start + total_written
        actual_end = clear_start + old_count - 1
        if actual_start > actual_end:
            return
        print(f"  Clearing excess rows {actual_start}-{actual_end}")
        self.clear(sheet_id, actual_start, actual_end, cols)


def safe_clear_range(token, spreadsheet_token, sheet_id, start_row, end_row, cols):
    """便捷函数：安全清空指定区域。"""
    writer = FeishuSheetWriter(spreadsheet_token, token)
    writer.clear(sheet_id, start_row, end_row, cols)


def safe_write_rows(token, spreadsheet_token, sheet_id, start_row, rows, cols):
    """便捷函数：安全写入数据行。"""
    writer = FeishuSheetWriter(spreadsheet_token, token)
    writer.write(sheet_id, start_row, rows, cols)
