"""
Excel workbook helpers for the Wuling dealer outreach skill.

Writes use openpyxl so the workbook structure, styles, data validations,
images, and non-target sheets are preserved.
"""
from __future__ import annotations

from copy import copy
from pathlib import Path
from typing import Any, Dict, List, Optional

import pandas as pd
from openpyxl import load_workbook

STANDARD_COLUMNS = [
    "序号", "客户姓名/公司", "国家", "城市", "客户属性", "客户类型",
    "主页/链接", "联系人", "职位", "电话/WhatsApp", "邮箱",
    "主营业务", "建联状态", "下次跟进", "备注",
]

FACEBOOK_COLUMNS = [*STANDARD_COLUMNS[:6], "公司官网", *STANDARD_COLUMNS[6:]]

LINKEDIN_COLUMNS = [
    "公司名称", "国家", "城市", "客户属性", "客户类型", "linkin链接",
    "联系人", "职位", "公司公共电话", "公司公共邮箱（任一有效即可）",
    "个人邮箱（不一定有效）", "公司主营业务", "建联状态", "备注",
]

GOOGLE_MAPS_COLUMNS = [
    "客户姓名/公司", "国家", "城市", "客户属性", "客户类型", "主页/链接",
    "联系人", "职位", "电话/WhatsApp", "邮箱", "主营业务",
    "建联状态", "下次跟进", "备注",
]

AUTO_WEBSITE_COLUMNS = [
    "序号", "客户姓名/公司", "国家", "城市", "客户属性", "客户类型", "主页/链接",
    "来源网站", "联系人", "职位", "电话/WhatsApp", "邮箱", "主营业务",
    "建联状态", "下次跟进", "备注",
]

FACEBOOK_CONVERSATION_COLUMNS = [
    "记录ID", "客户序号", "客户姓名/公司", "Facebook主页链接", "Messenger线程ID",
    "消息时间", "消息方向", "发件人", "原文语言", "对话原文", "中文翻译", "消息类型",
    "是否有效客户回复", "合作意向", "意向判断依据", "下一步建议", "同步时间",
    "来源账号/Profile ID", "风险标记",
]

COLUMN_ALIASES = {
    "客户姓名/公司": ["客户姓名/公司", "公司名称", "名称"],
    "公司名称": ["公司名称", "客户姓名/公司", "名称"],
    "主页/链接": ["主页/链接", "linkin链接", "LinkedIn链接", "链接", "网址"],
    "公司官网": ["公司官网", "官网", "官方网站", "Website", "Company Website"],
    "linkin链接": ["linkin链接", "LinkedIn链接", "主页/链接"],
    "电话/WhatsApp": ["电话/WhatsApp", "公司公共电话", "电话", "WhatsApp"],
    "公司公共电话": ["公司公共电话", "电话/WhatsApp", "电话"],
    "邮箱": ["邮箱", "公司公共邮箱（任一有效即可）", "公司公共邮箱", "公共邮箱"],
    "公司公共邮箱（任一有效即可）": ["公司公共邮箱（任一有效即可）", "公司公共邮箱", "公共邮箱", "邮箱"],
    "个人邮箱（不一定有效）": ["个人邮箱（不一定有效）", "个人邮箱"],
    "个人邮箱": ["个人邮箱", "个人邮箱（不一定有效）"],
    "主营业务": ["主营业务", "公司主营业务"],
    "公司主营业务": ["公司主营业务", "主营业务"],
    "建联状态": ["建联状态", "建联情况"],
    "建联情况": ["建联情况", "建联状态"],
}


def get_sheet_columns(sheet_name: str) -> List[str]:
    name = sheet_name.strip()
    if name == "Facebook":
        return FACEBOOK_COLUMNS
    if name == "LinkedIn":
        return LINKEDIN_COLUMNS
    if name == "Google Maps":
        return GOOGLE_MAPS_COLUMNS
    if name == "汽车网站精选线索":
        return AUTO_WEBSITE_COLUMNS
    if name == "Facebook对话记录":
        return FACEBOOK_CONVERSATION_COLUMNS
    return STANDARD_COLUMNS


def _aliases(column: str) -> List[str]:
    values = COLUMN_ALIASES.get(column, [column])
    return list(dict.fromkeys([column, *values]))


def _first_value(record: Dict[str, Any], column: str) -> Any:
    for key in _aliases(column):
        value = record.get(key, "")
        if value not in (None, ""):
            return value
    return ""


def _headers(ws) -> List[str]:
    values = []
    for cell in ws[1]:
        value = "" if cell.value is None else str(cell.value).strip()
        if value:
            values.append(value)
    return values


def _header_index(ws) -> Dict[str, int]:
    return {header: idx for idx, header in enumerate(_headers(ws), start=1)}


def _find_header(headers: Dict[str, int], column: str) -> Optional[str]:
    for alias in _aliases(column):
        if alias in headers:
            return alias
    return None


def _cell_value(row_values: Dict[str, Any], column: str) -> str:
    for alias in _aliases(column):
        value = row_values.get(alias, "")
        if value not in (None, ""):
            return str(value).strip()
    return ""


def _copy_row_style(ws, source_row: int, target_row: int) -> None:
    if source_row < 1 or source_row == target_row:
        return
    for col in range(1, ws.max_column + 1):
        source = ws.cell(source_row, col)
        target = ws.cell(target_row, col)
        if source.has_style:
            target._style = copy(source._style)
        if source.number_format:
            target.number_format = source.number_format
        if source.alignment:
            target.alignment = copy(source.alignment)
        if source.protection:
            target.protection = copy(source.protection)


def read_sheet(excel_path: str, sheet_name: str) -> pd.DataFrame:
    path = Path(excel_path)
    if not path.exists():
        raise FileNotFoundError(f"Excel 文件不存在: {excel_path}")

    df = pd.read_excel(excel_path, sheet_name=sheet_name)
    expected_cols = get_sheet_columns(sheet_name)
    for col in expected_cols:
        if col in df.columns:
            continue
        for alias in _aliases(col):
            if alias in df.columns:
                df[col] = df[alias]
                break
        else:
            df[col] = ""
    return df


def normalize_record(record: Dict[str, Any], sheet_name: str) -> Dict[str, Any]:
    normalized: Dict[str, Any] = {}
    for col in get_sheet_columns(sheet_name):
        normalized[col] = _first_value(record, col)
    return normalized


def append_records(
    excel_path: str,
    sheet_name: str,
    records: List[Dict[str, Any]],
    dedup_keys: Optional[List[str]] = None,
) -> Dict[str, Any]:
    if not records:
        return {"appended": 0, "skipped": 0, "total": 0}

    path = Path(excel_path)
    if not path.exists():
        raise FileNotFoundError(f"Excel 文件不存在: {excel_path}")

    wb = load_workbook(path)
    if sheet_name not in wb.sheetnames:
        ws = wb.create_sheet(sheet_name)
        for idx, header in enumerate(get_sheet_columns(sheet_name), start=1):
            ws.cell(1, idx, header)
    else:
        ws = wb[sheet_name]

    headers = _header_index(ws)
    if not headers:
        for idx, header in enumerate(get_sheet_columns(sheet_name), start=1):
            ws.cell(1, idx, header)
        headers = _header_index(ws)

    for expected_header in get_sheet_columns(sheet_name):
        if expected_header not in headers and not _find_header(headers, expected_header):
            next_col = ws.max_column + 1
            ws.cell(1, next_col, expected_header)
            headers[expected_header] = next_col

    keys = [key for key in (dedup_keys or []) if _find_header(headers, key)]
    existing_signatures = set()
    if keys:
        for row in ws.iter_rows(min_row=2, values_only=False):
            values = {header: row[col_idx - 1].value for header, col_idx in headers.items() if col_idx <= len(row)}
            sig = "|".join(_cell_value(values, key).casefold() for key in keys)
            if sig.strip("|"):
                existing_signatures.add(sig)

    appended = 0
    skipped = 0
    last_style_row = ws.max_row if ws.max_row > 1 else 1

    for record in records:
        normalized = normalize_record(record, sheet_name)
        if keys:
            sig = "|".join(str(_first_value(normalized, key)).strip().casefold() for key in keys)
            if sig.strip("|") and sig in existing_signatures:
                skipped += 1
                continue
            if sig.strip("|"):
                existing_signatures.add(sig)

        target_row = ws.max_row + 1
        _copy_row_style(ws, last_style_row, target_row)
        for header, col_idx in headers.items():
            if header.startswith("Unnamed"):
                continue
            value = _first_value(normalized, header)
            if header == "序号" and value == "":
                value = target_row - 1
            ws.cell(target_row, col_idx, value)
        appended += 1
        last_style_row = target_row

    wb.save(path)
    return {"appended": appended, "skipped": skipped, "total": max(ws.max_row - 1, 0)}


def update_status(
    excel_path: str,
    sheet_name: str,
    filters: Dict[str, Any],
    updates: Dict[str, Any],
) -> int:
    path = Path(excel_path)
    if not path.exists():
        raise FileNotFoundError(f"Excel 文件不存在: {excel_path}")

    wb = load_workbook(path)
    if sheet_name not in wb.sheetnames:
        raise ValueError(f"Sheet 不存在: {sheet_name}")
    ws = wb[sheet_name]
    headers = _header_index(ws)

    updated = 0
    for row_idx in range(2, ws.max_row + 1):
        row_values = {header: ws.cell(row_idx, col_idx).value for header, col_idx in headers.items()}
        matched = True
        for key, expected in filters.items():
            actual = _cell_value(row_values, key)
            if actual != str(expected):
                matched = False
                break
        if not matched:
            continue

        for key, value in updates.items():
            header = _find_header(headers, key)
            if header:
                ws.cell(row_idx, headers[header], value)
        updated += 1

    wb.save(path)
    return updated



