"""
生成图片 API - 供 pywebview 前端调用
流程：上传图片 -> workflow_run -> 轮询 query_result -> 解析返回图片 URL
"""

import base64
import json
import os
import tempfile
import time
import re
import traceback

import requests

from coze import upload_file, workflow_run, query_result
from config import workflow_id
from ali_oss import upload_data_urls as oss_upload_data_urls


def _data_url_to_temp_file(data_url: str, allow_video: bool = False) -> str:
    """将 base64 data URL 转为临时文件路径"""
    # data:image/png;base64,xxxx 或 data:video/mp4;base64,xxxx
    if allow_video:
        match = re.match(r'data:(?:image|video)/(\w+);base64,(.+)', data_url)
    else:
        match = re.match(r'data:image/(\w+);base64,(.+)', data_url)
    if not match:
        raise ValueError('无效的 data URL 格式')
    mime = match.group(1).lower()
    ext_map = {'png': 'png', 'webp': 'png', 'jpeg': 'jpg', 'jpg': 'jpg', 'mp4': 'mp4', 'webm': 'webm'}
    ext = ext_map.get(mime, 'jpg')
    data = base64.b64decode(match.group(2))
    fd, path = tempfile.mkstemp(suffix=f'.{ext}')
    try:
        os.write(fd, data)
    finally:
        os.close(fd)
    return path


def _upload_video(video_data_url: str) -> str:
    """上传视频，返回 file_id"""
    if not video_data_url or not isinstance(video_data_url, str):
        raise ValueError('无效的视频数据')
    path = _data_url_to_temp_file(video_data_url, allow_video=True)
    try:
        resp = upload_file(path)
        if resp.get('code') == 0 and resp.get('data', {}).get('id'):
            return resp['data']['id']
        raise RuntimeError(f'视频上传失败: {resp.get("msg", resp)}')
    finally:
        try:
            os.unlink(path)
        except Exception:
            pass


def _upload_images(image_data_urls: list) -> list:
    """上传多张图片，返回 file_id 列表"""
    file_ids = []
    temp_paths = []
    try:
        for data_url in (image_data_urls or []):
            if not data_url or not isinstance(data_url, str):
                continue
            if data_url.startswith("http"):
                fd, path = tempfile.mkstemp(suffix=f'.png')
                data_content = requests.get(data_url).content
                os.write(fd, data_content)
            else:
                path = _data_url_to_temp_file(data_url)
            temp_paths.append(path)
            resp = upload_file(path)
            if resp.get('code') == 0 and resp.get('data', {}).get('id'):
                file_ids.append(resp['data']['id'])
            else:
                raise RuntimeError(f'上传失败: {resp.get("msg", resp)}')
        return file_ids
    finally:
        for p in temp_paths:
            try:
                os.unlink(p)
            except Exception:
                pass


def _parse_output(output_str: str) -> list:
    """解析 query_result 中的 output，提取图片 URL 列表"""
    try:
        outer = json.loads(output_str)
        inner_str = outer.get('Output', '{}')
        inner = json.loads(inner_str)
        data_str = inner.get('data', '[]')
        data = json.loads(data_str)
        urls = data.get("images")
        long_image_url = data.get("merged_image_url")
        return urls if isinstance(urls, list) else [],long_image_url
    except Exception:
        return [],None


def _parse_output_text(output_str: str) -> list:
    """解析 query_result 中的 output，提取图片 URL 列表"""
    try:
        outer = json.loads(output_str)
        inner_str = outer.get('Output', '{}')
        inner = json.loads(inner_str)
        data_str = inner.get('data', '')
        if isinstance(data_str,str):
            data = json.loads(data_str).get("reverse_prompt")
            return [data]
        pattern = r'【?图像 \d+】.*?(?=【?图像 \d+】|$)'
        segments = re.findall(pattern, data_str, re.DOTALL)
        return segments
    except Exception:
        return ""


def _poll_until_done(wf_id: str, execute_id: str, interval: float =10, timeout: int = 600*2, is_text: bool = False) -> list:
    """轮询直到成功或超时，返回图片 URL 列表"""
    start = time.time()
    while time.time() - start < timeout:
        resp = query_result(wf_id, execute_id)
        if resp.get('code') != 0:
            raise RuntimeError(f'查询失败: {resp.get("msg", resp)}')
        items = resp.get('data') or []
        if not items:
            time.sleep(interval)
            continue
        item = items[0]
        status = item.get('execute_status', '')
        if status == 'Success':
            output = item.get('output', '{}')
            if is_text:
                return _parse_output_text(output)
            return _parse_output(output)
        if status and status not in ('Running', 'Pending', ''):
            raise RuntimeError(f'执行失败: {status}')
        time.sleep(interval)
    raise RuntimeError('生成超时')


def _res_to_2k(res: str) -> str:
    """统一分辨率为 2K/4K"""
    r = (res or '2k').strip().upper()
    return '4K' if r == '4K' else '2K'


def generate(params: dict) -> dict:
    """
    生成图片
    params: {
        menu: int,  # 1=图片反推 2=图片编辑 3=随机海报 4=克隆海报 5=服饰穿搭
        prompt: str,  # 用户自定义指令 (menu=1 必填)
        ref_images: list,  # base64 data URLs - 参考图
        video: str,  # base64 data URL - 视频 (menu=1 可选，仅支持1个)
        model_images: list,  # base64 data URLs - 多模特图 (menu!=1，最多5张)
        name: str, desc: str, ratio: str, resolution: str, count: int,
        language: str, style: str, batch_prompt: list, brand_name: str,
        Ingredients: str, activity: str, mode: str, texts: list,
        proc_images: list, layout_image: str,
    }
    返回: { success: bool, urls: list, prompts: list, error: str }
    """
    original_urls = []
    api_key = (params.get('api_key') or '').strip()
    try:
        menu = int(params.get('menu', 2))
        ref_data = params.get('ref_images') or []
        proc_data = params.get('proc_images') or []
        layout_data = params.get('layout_image')
        video_data = params.get('video')
        model_data = params.get('model_images') or []

        # menu 1: 反推词，简化参数
        if menu == 1:
            base_params = {'menu': 1, 'prompt': str(params.get('prompt', '')).strip() or ''}

            ref_ids = _upload_images(ref_data) if ref_data else []
            if ref_ids:
                base_params['ref_images'] = [{'file_id': fid} for fid in ref_ids]
            if video_data:
                try:
                    vid = _upload_video(video_data)
                    base_params['video'] = {'file_id': vid}
                except Exception as ve:
                    return {'success': False, 'urls': [], 'prompts': [], 'error': f'视频上传失败: {ve}'}
            base_params = {k: v for k, v in base_params.items() if v is not None and v != ''}
            base_params["api_key"] = api_key
            resp = workflow_run(workflow_id, base_params)
            if resp.get('code') != 0:
                return {'success': False, 'urls': [], 'prompts': [], 'error': resp.get('msg', str(resp))}
            execute_id = resp.get('execute_id')
            if not execute_id:
                return {'success': False, 'urls': [], 'prompts': [], 'error': '未返回 execute_id'}
            prompts = _poll_until_done(workflow_id, str(execute_id), is_text=True)
            # menu 1 返回提示词列表，统一转为字符串
            # prompts = [str(x) for x in result_list] if result_list else []
            # prompts = [prompts]
            return {'success': True, 'urls': [], 'prompts': prompts, 'error': ''}

        # menu != 1: 原有逻辑
        all_ref = list(ref_data)
        if layout_data:
            all_ref = [layout_data] + list(ref_data)
        all_originals = list(all_ref) + list(proc_data) + list(model_data)

        if all_originals:
            try:
                original_urls = oss_upload_data_urls(all_originals, prefix="originals")
            except Exception:
                traceback.print_exc()
                pass

        ref_ids = _upload_images(all_ref) if all_ref else []
        proc_ids = _upload_images(proc_data) if proc_data else []
        model_ids = _upload_images(model_data) if model_data else []

        res = _res_to_2k(params.get('resolution', '2K'))

        base_params = {
            'name': str(params.get('name', '')).strip(),
            'ratio': str(params.get('ratio', '')).strip(),
            'menu': menu,
            'resolution': res,
            'count': int(params.get('count', 1)),
            'desc': str(params.get('desc', '')).strip(),
            'language': str(params.get('language', '中文')).strip() or '中文',
            'style': str(params.get('style', '')).strip(),
            'prompt': str(params.get('prompt', '')).strip(),
            'batch_prompt': params.get('batch_prompt') or [],
            'brand_name': str(params.get('brand_name', '')).strip(),
            'Ingredients': str(params.get('Ingredients', '')).strip(),
            'activity': str(params.get('activity', '')).strip(),
            'mode': str(params.get('mode', '1')).strip() or '1',
            'texts': params.get('text') or [],
            "api_key" : api_key
        }

        if ref_ids:
            base_params['ref_images'] = [{'file_id': fid} for fid in ref_ids]
        if proc_ids:
            base_params['proc_images'] = [{'file_id': fid} for fid in proc_ids]
        if model_ids:
            base_params['model_images'] = [{'file_id': fid} for fid in model_ids]
        base_params = {k: v for k, v in base_params.items() if v is not None and v != ''}

        resp = workflow_run(workflow_id, base_params)
        if resp.get('code') != 0:
            return {'success': False, 'urls': [], 'error': resp.get('msg', str(resp))}
        execute_id = resp.get('execute_id')
        if not execute_id:
            return {'success': False, 'urls': [], 'error': '未返回 execute_id'}

        urls,long_image_url = _poll_until_done(workflow_id, str(execute_id))
        return {'success': True, 'urls': urls, 'original_urls': original_urls, 'error': '',"long_image_url":long_image_url}
    except Exception as e:
        return {'success': False, 'urls': [], 'prompts': [], 'original_urls': [], 'error': str(e),"long_image_url":""}


