from collections.abc import Callable
from dataclasses import dataclass
from datetime import datetime

from textual.app import App
from textual.css.query import NoMatches

from cli.tui.adapters import (
    TUI_SNAPSHOT_COLLECTOR_REGISTRY,
    BrowserPageSnapshot,
    DashboardSnapshot,
    DetailPageSnapshot,
)
from cli.tui.copy import TUI_COPY
from cli.tui.keymaps import TUI_KEYMAP_REGISTRY
from cli.tui.screens import BrowserScreen, DashboardScreen, DetailScreen
from cli.tui.widgets import NavigationItem, WorkspaceSidebar

SnapshotCollector = Callable[['ShierfengTuiApp'], BrowserPageSnapshot | DashboardSnapshot | DetailPageSnapshot]


@dataclass(frozen=True)
class TuiViewSpec:
    """
    TUI 视图规格定义。

    :param view_key: 视图标识
    :param include_query: 是否注入搜索词
    :param include_filter: 是否注入筛选键
    """

    view_key: str
    include_query: bool = False
    include_filter: bool = False


@dataclass(frozen=True)
class TuiViewDefinition:
    """
    TUI 视图定义。

    :param view_key: 视图标识
    :param collector: 页面快照采集器
    """

    view_key: str
    collector: SnapshotCollector


@dataclass(frozen=True)
class TuiViewRegistry:
    """
    TUI 视图注册表。

    该对象集中维护视图打开、刷新与顺序导航所需的元数据，
    避免 `ShierfengTuiApp` 内继续手工维护多份视图映射。

    :param definitions: 视图定义映射
    :param navigation_items: 导航项列表
    """

    definitions: dict[str, TuiViewDefinition]
    navigation_items: list[NavigationItem]

    @staticmethod
    def normalize_view_key(view_key: str) -> str:
        """
        规范化视图标识。

        :param view_key: 原始视图标识
        :return: 规范化后的视图标识
        """
        return str(view_key).strip().lower()

    def resolve_view_key(self, view_key: str) -> str:
        """
        解析可用视图标识，不存在时回退到 dashboard。

        :param view_key: 原始视图标识
        :return: 可用视图标识
        """
        normalized_view_key = self.normalize_view_key(view_key)
        if normalized_view_key in self.definitions:
            return normalized_view_key
        return 'dashboard'

    def collect_snapshot(
        self, app: 'ShierfengTuiApp', view_key: str
    ) -> BrowserPageSnapshot | DashboardSnapshot | DetailPageSnapshot:
        """
        采集指定视图的页面快照。

        :param app: 当前 TUI 应用实例
        :param view_key: 目标视图标识
        :return: 页面快照
        """
        resolved_view_key = self.resolve_view_key(view_key)
        return self.definitions[resolved_view_key].collector(app)

    def get_navigation_index(self, view_key: str) -> int:
        """
        获取视图在导航列表中的索引。

        :param view_key: 目标视图标识
        :return: 导航索引
        """
        resolved_view_key = self.resolve_view_key(view_key)
        return next(
            (index for index, item in enumerate(self.navigation_items) if item.view_key == resolved_view_key),
            0,
        )

    def get_relative_view_key(self, view_key: str, offset: int) -> str:
        """
        获取相对当前视图偏移后的目标视图标识。

        :param view_key: 当前视图标识
        :param offset: 偏移量
        :return: 目标视图标识
        """
        current_index = self.get_navigation_index(view_key)
        target_index = (current_index + offset) % len(self.navigation_items)
        return self.navigation_items[target_index].view_key


_TUI_VIEW_SPECS: tuple[TuiViewSpec, ...] = (
    TuiViewSpec('dashboard'),
    TuiViewSpec('app', include_query=True),
    TuiViewSpec('ops', include_query=True),
    TuiViewSpec('database', include_query=True),
    TuiViewSpec('cache', include_query=True),
    TuiViewSpec('jobs', include_query=True, include_filter=True),
    TuiViewSpec('gen', include_query=True),
    TuiViewSpec('configs', include_query=True, include_filter=True),
    TuiViewSpec('crypto', include_query=True),
)

_TUI_APP_CSS = """
Screen {
    layout: vertical;
    background: #04080f;
    color: #d8f7ff;
    scrollbar-size-horizontal: 1;
    scrollbar-size-vertical: 1;
    scrollbar-background: #06111b;
    scrollbar-background-hover: #0a1723;
    scrollbar-background-active: #0d1f2d;
    scrollbar-color: #1f6f8d;
    scrollbar-color-hover: #38d8ff;
    scrollbar-color-active: #6df7ff;
}

Widget {
    scrollbar-size-horizontal: 1;
    scrollbar-size-vertical: 1;
    scrollbar-background: #06111b;
    scrollbar-background-hover: #0a1723;
    scrollbar-background-active: #0d1f2d;
    scrollbar-color: #1f6f8d;
    scrollbar-color-hover: #38d8ff;
    scrollbar-color-active: #6df7ff;
}

#workspace-shell {
    height: 1fr;
    background: #050c15;
}

#workspace-main {
    padding: 1 2 1 1;
    background: #050c15;
    border: round #18425d;
    margin: 1 1 1 0;
    overflow-x: auto;
    overflow-y: auto;
    scrollbar-size-horizontal: 1;
    scrollbar-size-vertical: 1;
    scrollbar-background: #06111b;
    scrollbar-background-hover: #0a1723;
    scrollbar-background-active: #0d1f2d;
    scrollbar-color: #1f6f8d;
    scrollbar-color-hover: #38d8ff;
    scrollbar-color-active: #6df7ff;
}

#workspace-main:focus {
    border: heavy #6df7ff;
    background: #071320;
}

#detail-body {
    height: auto;
    margin-top: 1;
    border: double #18425d;
    background: #06111b;
    padding: 1;
    overflow-x: auto;
    overflow-y: auto;
}

#browser-body {
    height: auto;
    margin-top: 1;
    border: double #18425d;
    background: #06111b;
    padding: 1;
    overflow-x: auto;
    overflow-y: auto;
}

#browser-filter-bar {
    border: round #163c57;
    padding: 1 2;
    margin-bottom: 1;
    background: #07131f;
    color: #d9fbff;
}

#browser-detail-pane {
    width: 1fr;
    min-width: 48;
    border: round #163c57;
    padding: 1;
    background: #07131f;
    overflow-x: auto;
    overflow-y: auto;
}

#browser-detail-pane.is-ok {
    border: round #22c983;
}

#browser-detail-pane.is-fail {
    border: round #ff6b7a;
    background: #190d12;
}

#browser-detail-pane.is-warn {
    border: round #f2b84b;
    background: #171108;
}

#browser-detail-pane.is-info {
    border: round #38d8ff;
}

#browser-section-body {
    height: auto;
    border-top: solid #163c57;
    padding-top: 1;
    background: #06111b;
    overflow-x: auto;
    overflow-y: auto;
}

#dashboard-metrics {
    grid-size: 4;
    grid-gutter: 1 2;
    grid-columns: 1fr 1fr 1fr 1fr;
    height: auto;
    margin-top: 1;
    margin-bottom: 1;
    padding: 1 0;
}

#dashboard-grid, #detail-grid {
    grid-size: 2;
    grid-gutter: 1 2;
    grid-columns: 1fr 1fr;
    height: auto;
    margin-top: 1;
}

#detail-grid {
    margin-bottom: 1;
}

Footer {
    background: #060d16;
    color: #b6efff;
    border-top: heavy #163c57;
}

FooterKey {
    background: #0b2130;
    color: #ecfcff;
}
"""


@dataclass(frozen=True)
class TuiBindingSpec:
    """
    TUI 快捷键绑定定义。

    :param key: 快捷键
    :param action: 动作名称
    :param label: 展示标签
    """

    key: str
    action: str
    label: str


class TuiBindingBuilder:
    """
    TUI 快捷键绑定构建器。

    该对象负责统一装配应用级固定绑定和视图跳转绑定，避免 `ShierfengTuiApp`
    继续手工维护重复的绑定元组列表。
    """

    def build_base_bindings(self) -> list[TuiBindingSpec]:
        """
        构建应用级固定绑定。

        :return: 绑定定义列表
        """
        return [
            TuiBindingSpec('q', 'quit', TUI_COPY.build_app_binding_label('quit')),
            TuiBindingSpec('s', 'focus_sidebar', TUI_COPY.build_app_binding_label('sidebar')),
            TuiBindingSpec('[', 'show_previous_view', TUI_COPY.build_app_binding_label('previous')),
            TuiBindingSpec(']', 'show_next_view', TUI_COPY.build_app_binding_label('next')),
            TuiBindingSpec('r', 'refresh_current_view', TUI_COPY.build_app_binding_label('refresh')),
        ]

    def build_navigation_bindings(self) -> list[TuiBindingSpec]:
        """
        构建页面导航绑定。

        :return: 绑定定义列表
        """
        return [
            TuiBindingSpec(
                TUI_KEYMAP_REGISTRY.get_navigation_shortcut(spec.view_key),
                f'show_{spec.view_key}',
                TUI_COPY.render_view_label(spec.view_key),
            )
            for spec in _TUI_VIEW_SPECS
        ]

    def build(self) -> list[tuple[str, str, str]]:
        """
        构建 Textual 所需的绑定元组列表。

        :return: 绑定元组列表
        """
        return [
            (binding.key, binding.action, binding.label)
            for binding in [*self.build_base_bindings(), *self.build_navigation_bindings()]
        ]


TUI_APP_BINDINGS = TuiBindingBuilder().build()


@dataclass(frozen=True)
class TuiViewRegistryBuilder:
    """
    TUI 视图注册表构建器。

    该对象负责根据统一规格构建导航项和快照采集定义，避免模块内
    继续维护重复的 `lambda collect(...)` 配置块。
    """

    def build_navigation_items(self) -> list[NavigationItem]:
        """
        构建工作台导航项列表。

        :return: 导航项列表
        """
        return [
            NavigationItem(
                view_key,
                TUI_COPY.render_view_label(view_key),
                shortcut,
                TUI_COPY.render_navigation_description(view_key),
            )
            for view_key, shortcut in TUI_KEYMAP_REGISTRY.navigation_shortcuts.items()
        ]

    def build_collector(self, spec: TuiViewSpec) -> SnapshotCollector:
        """
        根据视图规格构建页面快照采集器。

        :param spec: 视图规格
        :return: 页面快照采集器
        """

        def collect(app: 'ShierfengTuiApp') -> BrowserPageSnapshot | DashboardSnapshot | DetailPageSnapshot:
            collect_kwargs: dict[str, str] = {}
            if spec.include_filter:
                collect_kwargs['filter_key'] = app.get_browser_filter(spec.view_key)
            if spec.include_query:
                collect_kwargs['query'] = app.get_browser_query(spec.view_key)
            return TUI_SNAPSHOT_COLLECTOR_REGISTRY.collect(spec.view_key, app.env, **collect_kwargs)

        return collect

    def build_definitions(self) -> dict[str, TuiViewDefinition]:
        """
        构建视图定义映射。

        :return: 视图定义映射
        """
        return {spec.view_key: TuiViewDefinition(spec.view_key, self.build_collector(spec)) for spec in _TUI_VIEW_SPECS}

    def build(self) -> TuiViewRegistry:
        """
        构建 TUI 视图注册表。

        :return: 视图注册表
        """
        return TuiViewRegistry(
            definitions=self.build_definitions(),
            navigation_items=self.build_navigation_items(),
        )


@dataclass
class TuiViewStateStore:
    """
    TUI 视图状态存储。

    该对象集中维护页面级动作反馈、浏览页筛选键和搜索词，避免
    `ShierfengTuiApp` 本体继续直接操作多份同类字典状态。

    :param action_feedback_by_view: 动作反馈映射
    :param browser_filter_by_view: 浏览页筛选映射
    :param browser_query_by_view: 浏览页搜索词映射
    """

    action_feedback_by_view: dict[str, list[str]]
    browser_filter_by_view: dict[str, str]
    browser_query_by_view: dict[str, str]

    @classmethod
    def create_default(cls) -> 'TuiViewStateStore':
        """
        创建默认视图状态存储。

        :return: 视图状态存储
        """
        return cls(
            action_feedback_by_view={},
            browser_filter_by_view={'jobs': 'all', 'configs': 'all'},
            browser_query_by_view={},
        )

    def remember_action_feedback(self, view_key: str, lines: list[str]) -> None:
        """
        记录指定页面最近一次动作反馈。

        :param view_key: 页面视图标识
        :param lines: 反馈文本行
        :return: None
        """
        self.action_feedback_by_view[view_key] = list(lines)

    def get_action_feedback_lines(self, view_key: str) -> list[str]:
        """
        读取指定页面最近一次动作反馈。

        :param view_key: 页面视图标识
        :return: 反馈文本行
        """
        return list(self.action_feedback_by_view.get(view_key, []))

    def remember_browser_filter(self, view_key: str, filter_key: str) -> None:
        """
        记录指定浏览页当前筛选键。

        :param view_key: 页面视图标识
        :param filter_key: 当前筛选键
        :return: None
        """
        self.browser_filter_by_view[view_key] = filter_key

    def get_browser_filter(self, view_key: str, default: str = 'all') -> str:
        """
        读取指定浏览页当前筛选键。

        :param view_key: 页面视图标识
        :param default: 默认筛选键
        :return: 当前筛选键
        """
        return str(self.browser_filter_by_view.get(view_key, default) or default)

    def remember_browser_query(self, view_key: str, query: str) -> None:
        """
        记录指定浏览页当前搜索词。

        :param view_key: 页面视图标识
        :param query: 当前搜索词
        :return: None
        """
        normalized_query = str(query or '').strip()
        if normalized_query:
            self.browser_query_by_view[view_key] = normalized_query
            return
        self.browser_query_by_view.pop(view_key, None)

    def get_browser_query(self, view_key: str, default: str = '') -> str:
        """
        读取指定浏览页当前搜索词。

        :param view_key: 页面视图标识
        :param default: 默认搜索词
        :return: 当前搜索词
        """
        return str(self.browser_query_by_view.get(view_key, default) or default)


TUI_VIEW_REGISTRY = TuiViewRegistryBuilder().build()
NAVIGATION_ITEMS = TUI_VIEW_REGISTRY.navigation_items


@dataclass(frozen=True)
class TuiScreenFactory:
    """
    TUI 工作台页面工厂。

    该对象负责把页面快照转换为具体 screen 实例，避免 `ShierfengTuiApp`
    同时承担状态协调和 screen 构建职责。
    """

    @staticmethod
    def build_refresh_timestamp() -> str:
        """
        构建当前页面刷新时间文本。

        :return: 格式化后的刷新时间
        """
        return datetime.now().strftime('%Y-%m-%d %H:%M:%S')

    def build(
        self,
        *,
        snapshot: BrowserPageSnapshot | DashboardSnapshot | DetailPageSnapshot,
        env: str,
        active_view: str,
        navigation_items: list[NavigationItem],
        action_feedback_lines: list[str],
    ) -> BrowserScreen | DashboardScreen | DetailScreen:
        """
        根据快照类型构建对应的工作台页面。

        :param snapshot: 页面快照
        :param env: 当前运行环境
        :param active_view: 当前激活视图
        :param navigation_items: 导航项列表
        :param action_feedback_lines: 当前视图动作反馈
        :return: 对应的页面对象
        """
        refreshed_at = self.build_refresh_timestamp()
        if isinstance(snapshot, DashboardSnapshot):
            return DashboardScreen(
                snapshot,
                env=env,
                active_view=active_view,
                navigation_items=navigation_items,
                refreshed_at=refreshed_at,
            )
        if isinstance(snapshot, BrowserPageSnapshot):
            return BrowserScreen(
                snapshot,
                env=env,
                active_view=active_view,
                navigation_items=navigation_items,
                refreshed_at=refreshed_at,
                action_feedback_lines=action_feedback_lines,
            )
        return DetailScreen(
            snapshot,
            env=env,
            active_view=active_view,
            navigation_items=navigation_items,
            refreshed_at=refreshed_at,
        )


@dataclass
class TuiScreenNavigator:
    """
    TUI 工作台页面导航器。

    该对象负责处理首屏展示与后续切屏差异，让应用壳更专注于状态切换。

    :param app: 当前 TUI 应用实例
    :param initialized: 工作台屏幕是否已初始化
    """

    app: 'ShierfengTuiApp'
    initialized: bool = False

    def show(self, screen: BrowserScreen | DashboardScreen | DetailScreen) -> None:
        """
        显示工作台页面，并兼容首屏初始化与后续切屏。

        Textual 在默认根屏阶段直接调用 `switch_screen()` 会触发内部回调栈异常，
        因此首次展示页面需要使用 `push_screen()` 建立活动屏，后续再改用
        `switch_screen()` 做同层页面替换。

        :param screen: 待显示的页面对象
        :return: None
        """
        if not self.initialized:
            self.app.push_screen(screen)
            self.initialized = True
            return
        self.app.switch_screen(screen)


@dataclass(frozen=True)
class TuiViewOpeningCoordinator:
    """
    TUI 视图打开协调器。

    该对象负责串联视图解析、快照采集、screen 构建与切屏展示，
    让 `ShierfengTuiApp` 更专注于状态暴露与 Textual action 桥接。

    :param view_registry: 视图注册表
    :param screen_factory: 工作台页面工厂
    :param navigation_items: 导航项列表
    """

    view_registry: TuiViewRegistry
    screen_factory: TuiScreenFactory
    navigation_items: list[NavigationItem]

    def show(self, app: 'ShierfengTuiApp', view_key: str) -> None:
        """
        切换并展示指定视图。

        :param app: 当前 TUI 应用实例
        :param view_key: 目标视图标识
        :return: None
        """
        resolved_view_key = self.view_registry.resolve_view_key(view_key)
        app.current_view = resolved_view_key
        snapshot = self.view_registry.collect_snapshot(app, resolved_view_key)
        screen = self.screen_factory.build(
            snapshot=snapshot,
            env=app.env,
            active_view=app.current_view,
            navigation_items=self.navigation_items,
            action_feedback_lines=app.get_action_feedback_lines(app.current_view),
        )
        app.screen_navigator.show(screen)


class ShierfengTuiApp(App[None]):
    """
    ShiErFeng CLI Textual 应用。

    :param env: 当前运行环境
    """

    CSS = _TUI_APP_CSS
    BINDINGS = TUI_APP_BINDINGS

    def __init__(self, env: str) -> None:
        """
        初始化 TUI 应用实例。

        :param env: 当前运行环境
        :return: None
        """
        self.env = env
        self.current_view = 'dashboard'
        self.view_state_store = TuiViewStateStore.create_default()
        self.screen_factory = TuiScreenFactory()
        self.screen_navigator = TuiScreenNavigator(self)
        self.view_opening_coordinator = TuiViewOpeningCoordinator(
            view_registry=TUI_VIEW_REGISTRY,
            screen_factory=self.screen_factory,
            navigation_items=NAVIGATION_ITEMS,
        )
        super().__init__()

    def show_view(self, view_key: str) -> None:
        """
        切换并展示指定视图。

        :param view_key: 目标视图标识
        :return: None
        """
        self.view_opening_coordinator.show(self, view_key)

    def remember_action_feedback(self, view_key: str, lines: list[str]) -> None:
        """
        记录指定页面最近一次动作反馈。

        :param view_key: 页面视图标识
        :param lines: 反馈文本行
        :return: None
        """
        self.view_state_store.remember_action_feedback(view_key, lines)

    def get_action_feedback_lines(self, view_key: str) -> list[str]:
        """
        读取指定页面最近一次动作反馈。

        :param view_key: 页面视图标识
        :return: 反馈文本行
        """
        return self.view_state_store.get_action_feedback_lines(view_key)

    def remember_browser_filter(self, view_key: str, filter_key: str) -> None:
        """
        记录指定浏览页当前筛选键。

        :param view_key: 页面视图标识
        :param filter_key: 当前筛选键
        :return: None
        """
        self.view_state_store.remember_browser_filter(view_key, filter_key)

    def get_browser_filter(self, view_key: str, default: str = 'all') -> str:
        """
        读取指定浏览页当前筛选键。

        :param view_key: 页面视图标识
        :param default: 默认筛选键
        :return: 当前筛选键
        """
        return self.view_state_store.get_browser_filter(view_key, default)

    def remember_browser_query(self, view_key: str, query: str) -> None:
        """
        记录指定浏览页当前搜索词。

        :param view_key: 页面视图标识
        :param query: 当前搜索词
        :return: None
        """
        self.view_state_store.remember_browser_query(view_key, query)

    def get_browser_query(self, view_key: str, default: str = '') -> str:
        """
        读取指定浏览页当前搜索词。

        :param view_key: 页面视图标识
        :param default: 默认搜索词
        :return: 当前搜索词
        """
        return self.view_state_store.get_browser_query(view_key, default)

    def on_mount(self) -> None:
        """
        在应用启动时载入首页。

        :return: None
        """
        self.show_view('dashboard')

    def open_view(self, view_key: str) -> None:
        """
        按视图标识打开对应页面。

        :param view_key: 视图标识
        :return: None
        """
        self.show_view(view_key)

    def action_focus_sidebar(self) -> None:
        """
        将焦点切换到左侧导航栏。

        :return: None
        """
        try:
            self.screen.query_one(WorkspaceSidebar).focus()
        except NoMatches:
            return

    def action_show_previous_view(self) -> None:
        """
        切换到前一个工作台视图。

        :return: None
        """
        self.open_view(TUI_VIEW_REGISTRY.get_relative_view_key(self.current_view, -1))

    def action_show_next_view(self) -> None:
        """
        切换到后一个工作台视图。

        :return: None
        """
        self.open_view(TUI_VIEW_REGISTRY.get_relative_view_key(self.current_view, 1))

    def action_refresh_current_view(self) -> None:
        """
        刷新当前视图数据。

        :return: None
        """
        self.show_view(self.current_view)

    def action_show_dashboard(self) -> None:
        """
        切换到首页视图。

        :return: None
        """
        self.open_view('dashboard')

    def action_show_app(self) -> None:
        """
        切换到应用详情视图。

        :return: None
        """
        self.open_view('app')

    def action_show_ops(self) -> None:
        """
        切换到运维详情视图。

        :return: None
        """
        self.open_view('ops')

    def action_show_database(self) -> None:
        """
        切换到数据库详情视图。

        :return: None
        """
        self.open_view('database')

    def action_show_cache(self) -> None:
        """
        切换到缓存浏览视图。

        :return: None
        """
        self.open_view('cache')

    def action_show_jobs(self) -> None:
        """
        切换到任务浏览视图。

        :return: None
        """
        self.open_view('jobs')

    def action_show_gen(self) -> None:
        """
        切换到代码生成浏览视图。

        :return: None
        """
        self.open_view('gen')

    def action_show_configs(self) -> None:
        """
        切换到配置浏览视图。

        :return: None
        """
        self.open_view('configs')

    def action_show_crypto(self) -> None:
        """
        切换到传输加密详情视图。

        :return: None
        """
        self.open_view('crypto')


@dataclass(frozen=True)
class TuiAppRunner:
    """
    TUI 应用启动器。

    该对象负责按环境创建并运行 Textual 应用实例，使命令入口不再依赖
    模块级启动函数。

    :param app_factory: TUI 应用工厂
    """

    app_factory: Callable[[str], ShierfengTuiApp]

    def run(self, env: str) -> None:
        """
        启动 TUI 应用。

        :param env: 当前运行环境
        :return: None
        """
        self.app_factory(env).run()


TUI_APP_RUNNER = TuiAppRunner(ShierfengTuiApp)
