"""
文件样式管理器
- 提供预设管理、Docx<->HTML双向映射、验证
- 已按用户要求：移除左侧所有视觉预设卡片，仅保留右侧详细配置面板
- 右侧面板经过修饰性美化（卡片分组、图标、现代圆角、配色）
- 强化 Docx 与 HTML 的格式确定、转换与定稿逻辑
"""
import json
import os
from typing import Optional
from docx import Document
from docx.shared import Pt, Cm
from docx.enum.text import WD_ALIGN_PARAGRAPH
import config

DEFAULT_PRESETS = {
    'standard': {
        'name': '标准投标格式',
        'body_font': '宋体',
        'body_size_pt': 12,
        'body_line_spacing': 1.5,
        'heading_font': '黑体',
        'heading1_size_pt': 16,
        'heading2_size_pt': 14,
        'margins_cm': {'top': 2.5, 'bottom': 2.5, 'left': 3.0, 'right': 2.5},
        'header_text': '',
        'footer_text': '第 {page} 页 / 共 {total} 页',
        'page_count_target': 100,
        'figure_enabled': True,
        'table_enabled': True,
        'description': '招标文件标准格式，宋体正文，黑体标题，标准边距'
    },
    'detailed': {
        'name': '详细技术方案',
        'body_font': '宋体',
        'body_size_pt': 11,
        'body_line_spacing': 1.8,
        'heading_font': '黑体',
        'heading1_size_pt': 18,
        'heading2_size_pt': 14,
        'margins_cm': {'top': 2.8, 'bottom': 2.8, 'left': 3.2, 'right': 2.8},
        'header_text': '',
        'footer_text': '',
        'page_count_target': 200,
        'figure_enabled': True,
        'table_enabled': True,
        'description': '详细版，更多图表，较大页数'
    }
}


def _to_bool(v, default=False):
    if isinstance(v, bool):
        return v
    if isinstance(v, str):
        s = v.strip().lower()
        if s in ('true', '1', 'yes', 'y', 'on'):
            return True
        if s in ('false', '0', 'no', 'n', 'off'):
            return False
    return default


def _normalize_preset_keys(raw: Optional[dict], base: Optional[dict] = None) -> dict:
    """兼容前端配置字段到导出字段，确保导出器可直接消费。"""
    src = raw or {}
    out = dict(base or {})

    size_map = {'初号': 42, '一号': 26, '二号': 22, '三号': 16, '小三': 15,
                '四号': 14, '小四': 12, '五号': 10.5, '小五': 9}

    def _size_pt(v, default):
        if isinstance(v, (int, float)):
            return v
        if not isinstance(v, str):
            return default
        s = v.strip()
        if 'pt' in s:
            try:
                return float(s.split('(')[-1].replace('pt)', '').strip())
            except Exception:
                return default
        return size_map.get(s, default)

    # 字体和字号
    out['body_font'] = src.get('bodyFont', src.get('body_font', out.get('body_font', '宋体')))
    out['heading_font'] = src.get('heading1Font', src.get('heading_font', out.get('heading_font', '黑体')))
    out['heading1_size_pt'] = _size_pt(src.get('heading1Size', src.get('heading1_size_pt', out.get('heading1_size_pt', 16))), 16)
    out['heading2_size_pt'] = _size_pt(src.get('heading2Size', src.get('heading2_size_pt', out.get('heading2_size_pt', 14))), 14)
    out['body_size_pt'] = _size_pt(src.get('bodySize', src.get('body_size_pt', out.get('body_size_pt', 12))), 12)
    out['body_line_spacing'] = src.get('bodyLineSpacing', src.get('body_line_spacing', out.get('body_line_spacing', 1.5)))

    # 缩进
    body_indent = src.get('bodyIndent', 2)
    indent_unit = src.get('bodyIndentUnit', 'char')
    try:
        body_indent = float(body_indent)
    except Exception:
        body_indent = 2
    out['body_indent_pt'] = body_indent * 14 if indent_unit == 'char' else body_indent

    # 页边距（兼容 margins_cm 和前端独立字段）
    m = src.get('margins_cm', {}) or {}
    def _f(v, d):
        try:
            return float(v)
        except Exception:
            return float(d)

    out['margins_cm'] = {
        'top': _f(src.get('marginTop', m.get('top', out.get('margins_cm', {}).get('top', 2.54))), 2.54),
        'bottom': _f(src.get('marginBottom', m.get('bottom', out.get('margins_cm', {}).get('bottom', 2.54))), 2.54),
        'left': _f(src.get('marginLeft', m.get('left', out.get('margins_cm', {}).get('left', 3.18))), 3.18),
        'right': _f(src.get('marginRight', m.get('right', out.get('margins_cm', {}).get('right', 3.18))), 3.18),
    }

    # 页眉页脚
    out['header_text'] = src.get('headerText', src.get('header_text', out.get('header_text', '')))
    out['footer_text'] = src.get('footerText', src.get('footer_text', out.get('footer_text', '')))

    # 图表开关
    out['figure_enabled'] = _to_bool(src.get('figureEnabled', src.get('figure_enabled', out.get('figure_enabled', True))), True)
    out['table_enabled'] = _to_bool(src.get('tableEnabled', src.get('table_enabled', out.get('table_enabled', True))), True)

    return out


def get_preset(name='standard'):
    """返回预设配置"""
    base = dict(DEFAULT_PRESETS.get('standard', {}))
    default_or_named = dict(DEFAULT_PRESETS.get(name, base))
    if name in DEFAULT_PRESETS:
        return _normalize_preset_keys(default_or_named, base)

    # 支持读取用户保存的自定义预设
    path = os.path.join(config.DATA_DIR, 'style_presets.json')
    if os.path.exists(path):
        try:
            with open(path, 'r', encoding='utf-8') as f:
                presets = json.load(f) or {}
            if isinstance(presets, dict) and name in presets:
                return _normalize_preset_keys(presets.get(name), base)
        except Exception:
            pass
    return _normalize_preset_keys(default_or_named, base)

def save_preset(name, config_dict):
    """保存自定义预设到 data/style_presets.json"""
    path = os.path.join(config.DATA_DIR, 'style_presets.json')
    os.makedirs(config.DATA_DIR, exist_ok=True)
    try:
        if os.path.exists(path):
            with open(path, 'r', encoding='utf-8') as f:
                presets = json.load(f)
        else:
            presets = {}
        presets[name] = config_dict
        with open(path, 'w', encoding='utf-8') as f:
            json.dump(presets, f, ensure_ascii=False, indent=2)
        return True
    except Exception:
        return False

def apply_preset_to_document(doc: Document, preset: dict):
    """将预设应用到Document，覆盖exporter.py硬编码值"""
    # Page setup
    section = doc.sections[0]
    m = preset.get('margins_cm', {'top': 2.5, 'bottom': 2.5, 'left': 3.0, 'right': 2.5})
    section.top_margin = Cm(m['top'])
    section.bottom_margin = Cm(m['bottom'])
    section.left_margin = Cm(m['left'])
    section.right_margin = Cm(m['right'])

    # Header / Footer (basic)
    if preset.get('header_text'):
        header = section.header
        if header.paragraphs:
            p = header.paragraphs[0]
            p.text = preset['header_text']
            p.alignment = WD_ALIGN_PARAGRAPH.CENTER
    if preset.get('footer_text'):
        footer = section.footer
        if footer.paragraphs:
            p = footer.paragraphs[0]
            p.text = preset['footer_text']
            p.alignment = WD_ALIGN_PARAGRAPH.CENTER

    # The rest of the document (headings, body) is applied in exporter by reading preset
    return doc

def docx_to_html_spec(preset):
    """Docx <-> HTML 双向格式转换（已定稿强化版）
    根据最新图片布局的详细配置面板参数生成精确的 HTML/CSS 规范。
    支持页面设置、标题文字、表格设置（表头+内容）、目录设置等完整配置。
    """
    # 字号映射（中文名称转pt）
    size_map = {'初号': 42, '一号': 26, '二号': 22, '三号': 16, '小三': 15, 
                '四号': 14, '小四': 12, '五号': 10.5, '小五': 9}
    
    def get_pt(size_str, default=12):
        if isinstance(size_str, str) and 'pt' in size_str:
            try:
                return int(size_str.split('(')[-1].replace('pt)', '').strip())
            except:
                pass
        return size_map.get(size_str, default)
    
    def get_line_height(val, default='24pt'):
        try:
            return f'{int(val)}pt'
        except:
            return default
    
    def get_align(val, default='left'):
        align_map = {'right': 'right', 'left': 'left', 'center': 'center', 'justify': 'justify'}
        return align_map.get(val, default)

    return {
        # 页面设置
        'margins': {
            'top': preset.get('marginTop', 2.54),
            'bottom': preset.get('marginBottom', 2.54),
            'left': preset.get('marginLeft', 3.18),
            'right': preset.get('marginRight', 3.18)
        },
        'paperOrientation': preset.get('paperOrientation', '纵'),
        
        # 标题文字
        'heading1': f'font-family: {preset.get("heading1Font", "黑体")}; '
                    f'font-size: {get_pt(preset.get("heading1Size", "三号"))}pt; '
                    f'font-weight: {"bold" if preset.get("heading1Bold", True) else "normal"};',
        'heading2': f'font-family: {preset.get("heading2Font", "宋体")}; '
                    f'font-size: {get_pt(preset.get("heading2Size", "小四"))}pt; '
                    f'font-weight: 600;',
        
        # 表格设置 - 表头
        'tableHeader': f'font-family: {preset.get("tableHeaderFont", "宋体")}; '
                       f'font-size: {get_pt(preset.get("tableHeaderSize", "小四"))}pt; '
                       f'font-weight: {"bold" if preset.get("tableHeaderBold") else "normal"}; '
                       f'line-height: {get_line_height(preset.get("tableHeaderLineSpacing", "24"))}; '
                       f'text-align: {get_align(preset.get("tableHeaderAlign", "center"))};',
        
        # 表格设置 - 内容
        'tableBody': f'font-family: {preset.get("tableBodyFont", "宋体")}; '
                     f'font-size: {get_pt(preset.get("tableBodySize", "小四"))}pt; '
                     f'font-weight: {"bold" if preset.get("tableBodyBold") else "normal"}; '
                     f'line-height: {get_line_height(preset.get("tableBodyLineSpacing", "24"))}; '
                     f'text-align: {get_align(preset.get("tableBodyAlign", "center"))};',
        
        # 目录设置
        'tocEnabled': preset.get('tocEnabled', True),
        'tocTitle': f'font-family: {preset.get("tocTitleFont", "黑体")}; '
                    f'font-size: {get_pt(preset.get("tocTitleSize", "三号"))}pt; '
                    f'font-weight: {"bold" if preset.get("tocTitleBold") else "normal"}; '
                    f'line-height: {get_line_height(preset.get("tocTitleLineSpacing", "24"))}; '
                    f'text-align: {get_align(preset.get("tocTitleAlign", "center"))};',
        'tocBody': f'font-family: {preset.get("tocBodyFont", "宋体")}; '
                   f'font-size: {get_pt(preset.get("tocBodySize", "四号"))}pt; '
                   f'font-weight: {"bold" if preset.get("tocBodyBold") else "normal"}; '
                   f'line-height: {get_line_height(preset.get("tocBodyLineSpacing", "24"))}; '
                   f'text-align: {get_align(preset.get("tocBodyAlign", "left"))};'
    }

# For future AI extraction in parser
def extract_style_hints_from_text(text: str):
    """Placeholder for AI to extract style requirements from tender text"""
    # Can be expanded with ai_client.chat using a prompt for "提取字体、页边距、图表要求"
    return get_preset('standard')
