from typing import List, Optional, Union

from flow_xhs.runtime.enums import Platform, XsecSource
from flow_xhs.runtime.errors import PublishExecutionError, PublishNoteException
from flow_xhs.runtime.accounts.manager import AccountLeaseManager, AccountManager
from flow_xhs.runtime.browser.manager import browser_manager
from flow_xhs.platform.factory import PlatformFactory
from flow_xhs.platform.base import RiskControlException
from flow_xhs.runtime.publishing.media import MediaPreprocessor
from flow_xhs.runtime.publishing.models import PublishResult, PublishStage, PublishTarget
from flow_xhs.runtime.publishing import normalize_publish_request


def _normalize_xsec_source(xsec_source: Union[XsecSource, str, None]) -> XsecSource:
    if isinstance(xsec_source, XsecSource):
        return xsec_source
    if isinstance(xsec_source, str) and xsec_source:
        for source in XsecSource:
            if source.value == xsec_source:
                return source
    return XsecSource.PC_FEED


def _normalize_platform(platform: Union[Platform, str, None]) -> Platform:
    if isinstance(platform, Platform):
        return platform
    if isinstance(platform, str) and platform:
        for item in Platform:
            if item.value == platform:
                return item
    return Platform.REDNOTE


def _strip_sensitive_account_fields(accounts: List[dict]) -> List[dict]:
    cleaned = []
    for account in accounts:
        item = dict(account)
        item.pop("storage_state", None)
        cleaned.append(item)
    return cleaned


def _build_publish_result_from_raw(raw_result) -> PublishResult:
    if isinstance(raw_result, dict):
        uploaded_count = int(raw_result.get("uploaded_count", 0) or 0)
        status = str(raw_result.get("status", "")).strip().lower()
        if status in {"success", "verified"}:
            return PublishResult(
                success=True,
                stage=PublishStage.VERIFIED,
                uploaded_count=uploaded_count,
                publish_url=str(raw_result.get("publish_url") or ""),
                publish_id=str(raw_result.get("publish_id") or ""),
                message=str(raw_result.get("message") or ""),
                data=raw_result,
            )
        if status == "uploaded":
            return PublishResult(
                success=False,
                stage=PublishStage.UPLOAD_DONE,
                uploaded_count=uploaded_count,
                publish_url=str(raw_result.get("publish_url") or ""),
                publish_id=str(raw_result.get("publish_id") or ""),
                message=str(raw_result.get("message") or "素材已上传，但未完成发布"),
                data=raw_result,
            )
        if status == "submitted":
            return PublishResult(
                success=False,
                stage=PublishStage.SUBMITTED,
                uploaded_count=uploaded_count,
                publish_url=str(raw_result.get("publish_url") or ""),
                publish_id=str(raw_result.get("publish_id") or ""),
                message=str(raw_result.get("message") or "发布动作已提交，但未验证是否真正发布成功"),
                data=raw_result,
            )
        return PublishResult(
            success=False,
            stage=PublishStage.FAILED,
            uploaded_count=uploaded_count,
            message=str(raw_result.get("message") or "发布失败"),
            data=raw_result,
        )

    return PublishResult(
        success=False,
        stage=PublishStage.FAILED,
        message=f"无法识别的发布返回值类型: {type(raw_result).__name__}",
    )


async def search_notes(
        keyword: str,
        size: int = 20,
        sort_by: Optional[str] = None,
        note_type: Optional[str] = None,
        publish_time: Optional[str] = None,
        search_scope: Optional[str] = None,
        location: Optional[str] = None,
        account_uid: Optional[str] = None,
        platform: Union[Platform, str, None] = Platform.REDNOTE
) -> List[dict]:
    platform_enum = _normalize_platform(platform)
    async with AccountLeaseManager.lease_account(platform_enum, account_uid) as account:
        storage_state = account.get("storage_state")
        async with browser_manager.get_page(storage_state, account_key=account.get("user_no")) as page:
            extractor = PlatformFactory.get_extractor(platform_enum, page)
            notes = await extractor.search_notes(
                keyword=keyword,
                size=size,
                sort_by=sort_by,
                note_type=note_type,
                publish_time=publish_time,
                search_scope=search_scope,
                location=location,
            )
            return notes if isinstance(notes, list) else []


async def search_users(
        keyword: str,
        size: int = 20,
        account_uid: Optional[str] = None,
        platform: Union[Platform, str, None] = Platform.REDNOTE
) -> List[dict]:
    platform_enum = _normalize_platform(platform)
    async with AccountLeaseManager.lease_account(platform_enum, account_uid) as account:
        storage_state = account.get("storage_state")
        async with browser_manager.get_page(storage_state, account_key=account.get("user_no")) as page:
            extractor = PlatformFactory.get_extractor(platform_enum, page)
            users = await extractor.search_users(keyword, size)
            return users if isinstance(users, list) else []


async def get_user_info(
        user_id: str,
        xsec_token: Optional[str] = None,
        xsec_source: Union[XsecSource, str, None] = XsecSource.PC_FEED,
        account_uid: Optional[str] = None,
        platform: Union[Platform, str, None] = Platform.REDNOTE
) -> dict:
    platform_enum = _normalize_platform(platform)
    normalized_source = _normalize_xsec_source(xsec_source)
    async with AccountLeaseManager.lease_account(platform_enum, account_uid) as account:
        storage_state = account.get("storage_state")
        async with browser_manager.get_page(storage_state, account_key=account.get("user_no")) as page:
            extractor = PlatformFactory.get_extractor(platform_enum, page)
            return await extractor.get_user_info(user_id, xsec_token, normalized_source)


async def get_note_info(
        note_id: str,
        xsec_token: Optional[str] = None,
        xsec_source: Union[XsecSource, str, None] = XsecSource.PC_FEED,
        account_uid: Optional[str] = None,
        comment_size: int = 10,
        sub_comment_size: int = 5,
        platform: Union[Platform, str, None] = Platform.REDNOTE,
) -> dict:
    platform_enum = _normalize_platform(platform)
    normalized_source = _normalize_xsec_source(xsec_source)
    async with AccountLeaseManager.lease_account(platform_enum, account_uid) as account:
        storage_state = account.get("storage_state")
        async with browser_manager.get_page(storage_state, account_key=account.get("user_no")) as page:
            extractor = PlatformFactory.get_extractor(platform_enum, page)
            return await extractor.get_note_info(
                note_id=note_id,
                xsec_token=xsec_token,
                xsec_source=normalized_source,
                comment_size=comment_size,
                sub_comment_size=sub_comment_size,
            )


async def get_self_info(
        account_uid: Optional[str] = None,
        platform: Union[Platform, str, None] = Platform.REDNOTE,
) -> dict:
    platform_enum = _normalize_platform(platform)
    async with AccountLeaseManager.lease_account(platform_enum, account_uid) as account:
        storage_state = account.get("storage_state")
        async with browser_manager.get_page(storage_state, account_key=account.get("user_no")) as page:
            extractor = PlatformFactory.get_extractor(platform_enum, page)
            return await extractor.get_self_info()


async def publish_note(
        target: str,
        image_list: Optional[list] = None,
        title: str = "",
        content: str = "",
        tags: Optional[list] = None,
        schedule_at: Optional[str] = None,
        account_uid: Optional[str] = None,
        platform: Union[Platform, str, None] = Platform.REDNOTE,
) -> dict:
    platform_enum = _normalize_platform(platform)
    request = normalize_publish_request(
        target=target,
        title=title,
        content=content,
        media_list=image_list,
        tags=tags,
        schedule_at=schedule_at,
        account_uid=account_uid,
    )
    stage = PublishStage.INIT
    try:
        async with MediaPreprocessor() as preprocessor:
            if request.target == PublishTarget.IMAGE:
                prepared_media = await preprocessor.prepare_images(request.media_list)
            elif request.target == PublishTarget.VIDEO:
                prepared_media = await preprocessor.prepare_videos(request.media_list)
            else:
                prepared_media = list(request.media_list or [])
            stage = PublishStage.MEDIA_READY

            async with AccountLeaseManager.lease_account(platform_enum, request.account_uid) as account:
                storage_state = account.get("storage_state")
                account_key = account.get("user_no")
                async with browser_manager.get_page(storage_state, account_key=account_key) as page:
                    extractor = PlatformFactory.get_extractor(platform_enum, page)
                    raw_result = await extractor.publish_note(
                        target=request.target.value,
                        image_list=prepared_media,
                        title=request.title,
                        content=request.content,
                        tags=request.tags,
                        schedule_at=request.schedule_at,
                    )
                    result = _build_publish_result_from_raw(raw_result)
                    if not result.success:
                        raise PublishExecutionError(result.message or f"发布未完成(stage={result.stage.value})")
                    if result.stage == PublishStage.UPLOAD_DONE:
                        result.data["pipeline_stage"] = PublishStage.UPLOAD_DONE.value
                    return result.to_dict()
    except RiskControlException:
        raise
    except PublishNoteException:
        raise
    except Exception as exc:
        raise PublishExecutionError(f"发布编排执行失败(stage={stage.value}): {exc}") from exc


async def list_accounts(
        platform: str = Platform.REDNOTE.value,
        only_active: bool = True
) -> List[dict]:
    accounts = AccountManager.list_accounts(only_active=only_active)
    filtered = [acc for acc in accounts if acc.get("platform") == platform]
    return _strip_sensitive_account_fields(filtered)
