# #region MaintenanceChartManager [C:3] [TYPE Module] [SEMANTICS maintenance, chart, banner, lifecycle]
# @BRIEF Chart operations for maintenance banners: create/get banner charts, process
#   per-dashboard state transitions for start and end orchestrators.
# @LAYER Service
# @RELATION DEPENDS_ON -> [SupersetDashboardsWriteMixin]
# @RELATION DEPENDS_ON -> [MaintenanceModels]

from typing import Any

from sqlalchemy.orm import Session

from ...core.logger import belief_scope, logger as app_logger
from ...core.superset_client import SupersetClient
from ...models.maintenance import (
    MaintenanceDashboardBanner,
    MaintenanceDashboardBannerStatus,
    MaintenanceDashboardState,
    MaintenanceDashboardStateStatus,
)
from ._banner_renderer import _build_banner_text_for_dashboard, rebuild_banner
from ._dashboard_scanner import _resolve_dashboard_title


# #region ensure_banner_chart [C:3] [TYPE Function] [SEMANTICS maintenance, banner, chart, creation]
# @BRIEF Get or create a MaintenanceDashboardBanner for a dashboard in the target environment.
#   If an active banner exists in DB, return it. Otherwise, create a markdown chart in Superset,
#   update dashboard layout, and store the banner row.
# @PRE dashboard_id exists in Superset. superset_client has write access.
# @POST Returns MaintenanceDashboardBanner with chart_id set. Chart is placed at top of dashboard.
# @SIDE_EFFECT Creates chart in Superset, modifies dashboard layout, writes DB row.
# @RELATION DEPENDS_ON -> [SupersetDashboardsWriteMixin]
def ensure_banner_chart(
    dashboard_id: int,
    environment_id: str,
    superset_client: SupersetClient,
    db_session: Session,
) -> MaintenanceDashboardBanner:
    with belief_scope(
        "ensure_banner_chart",
        f"dashboard_id={dashboard_id}",
    ):
        app_logger.reason(
            "Ensuring banner chart exists",
            extra={"dashboard_id": dashboard_id},
        )

        # Check for existing active banner
        existing = (
            db_session.query(MaintenanceDashboardBanner)
            .filter(
                MaintenanceDashboardBanner.dashboard_id == dashboard_id,
                MaintenanceDashboardBanner.environment_id == environment_id,
                MaintenanceDashboardBanner.status == MaintenanceDashboardBannerStatus.ACTIVE,
            )
            .first()
        )
        if existing:
            app_logger.reason(
                "Found existing active banner, verifying chart in Superset",
                extra={"banner_id": existing.id, "chart_id": existing.chart_id},
            )
            # Verify the chart still exists in Superset
            try:
                superset_client.get_chart(existing.chart_id)
                app_logger.reflect(
                    "Existing banner chart is alive, reusing",
                    extra={"banner_id": existing.id, "chart_id": existing.chart_id},
                )
                return existing
            except Exception:
                app_logger.explore(
                    "Existing banner chart is stale (not found in Superset), marking as REMOVED",
                    extra={
                        "banner_id": existing.id,
                        "chart_id": existing.chart_id,
                    },
                )
                existing.status = MaintenanceDashboardBannerStatus.REMOVED
                db_session.flush()
                # Fall through to create a new chart

        # Create markdown chart in Superset
        try:
            chart_id = superset_client.create_markdown_chart(
                dashboard_id, "*Maintenance banner placeholder*"
            )
        except Exception as e:
            app_logger.explore(
                "Failed to create markdown chart",
                extra={"dashboard_id": dashboard_id},
                error=str(e),
            )
            raise

        # Insert chart at top of dashboard layout
        try:
            superset_client.update_dashboard_layout(dashboard_id, chart_id)
        except Exception as e:
            app_logger.explore(
                "Failed to update dashboard layout, deleting chart",
                extra={"dashboard_id": dashboard_id, "chart_id": chart_id},
                error=str(e),
            )
            # Clean up the chart we just created
            try:
                superset_client.delete_chart(chart_id)
            except Exception:
                pass
            raise

        # Persist banner row
        banner = MaintenanceDashboardBanner(
            environment_id=environment_id,
            dashboard_id=dashboard_id,
            chart_id=chart_id,
            banner_text="",
            status=MaintenanceDashboardBannerStatus.ACTIVE,
        )
        db_session.add(banner)
        db_session.flush()

        app_logger.reflect(
            "Banner chart created and persisted",
            extra={
                "banner_id": banner.id,
                "chart_id": chart_id,
                "dashboard_id": dashboard_id,
            },
        )
        return banner
# #endregion ensure_banner_chart


# #region _process_dashboards_for_start [C:3] [TYPE Function]
# @BRIEF Process each dashboard for a start maintenance event: ensure banner chart,
#   create dashboard state, build banner text, update chart markdown.
# @SIDE_EFFECT Creates/updates Superset charts, writes DB rows.
# @RELATION CALLS -> [ensure_banner_chart]
# @RELATION CALLS -> [_build_banner_text_for_dashboard]
# @RELATION CALLS -> [_resolve_dashboard_title]
def _process_dashboards_for_start(
    dashboard_ids: list[int],
    event_id: str,
    environment_id: str,
    template: str,
    timezone_str: str,
    superset_client: SupersetClient,
    db_session: Session,
) -> tuple[list[dict[str, Any]], list[dict[str, Any]], list[MaintenanceDashboardBanner]]:
    successful_dashboards: list[dict[str, Any]] = []
    failed_dashboards: list[dict[str, Any]] = []
    banners_created: list[MaintenanceDashboardBanner] = []

    for dash_id in dashboard_ids:
        app_logger.reason(
            "Processing dashboard",
            extra={"dashboard_id": dash_id, "event_id": event_id},
        )
        try:
            # Ensure banner chart exists
            banner = ensure_banner_chart(
                dash_id,
                environment_id,
                superset_client,
                db_session,
            )
            banners_created.append(banner)

            # Create dashboard state
            state = MaintenanceDashboardState(
                event_id=event_id,
                dashboard_id=dash_id,
                banner_id=banner.id,
                status=MaintenanceDashboardStateStatus.ACTIVE,
            )
            db_session.add(state)
            db_session.flush()  # Ensure state is visible to subsequent queries

            # Build banner text for this dashboard's active events
            banner_text = _build_banner_text_for_dashboard(
                banner.id, db_session, template, timezone_str
            )

            # Update banner content on dashboard (native MARKDOWN in layout)
            if banner.chart_id:
                try:
                    superset_client.update_banner_on_dashboard(
                        dash_id, banner.chart_id, banner_text
                    )
                except Exception as e:
                    app_logger.explore(
                        "Failed to update banner on dashboard",
                        extra={
                            "chart_id": banner.chart_id,
                            "dashboard_id": dash_id,
                        },
                        error=str(e),
                    )
                    state.status = MaintenanceDashboardStateStatus.APPLY_FAILED
                    failed_dashboards.append(
                        {"id": dash_id, "title": str(dash_id), "error": str(e)}
                    )
                    continue

            # Update banner text in DB
            banner.banner_text = banner_text
            db_session.flush()

            # Resolve dashboard title
            title = _resolve_dashboard_title(dash_id, superset_client)
            successful_dashboards.append(
                {
                    "id": dash_id,
                    "title": title,
                    "chart_id": banner.chart_id,
                }
            )
            app_logger.reflect(
                "Dashboard processed successfully",
                extra={"dashboard_id": dash_id},
            )

        except Exception as e:
            app_logger.explore(
                "Failed to process dashboard",
                extra={"dashboard_id": dash_id},
                error=str(e),
            )
            failed_dashboards.append(
                {"id": dash_id, "title": str(dash_id), "error": str(e)}
            )

    return successful_dashboards, failed_dashboards, banners_created
# #endregion _process_dashboards_for_start


# #region _process_states_for_end [C:3] [TYPE Function]
# @BRIEF Process each dashboard state for an end maintenance event: if other events
#   still use same banner → rebuild; if no other events → remove banner (chart + layout).
# @SIDE_EFFECT Modifies Superset dashboards (removes charts, updates layouts). Writes DB.
# @RELATION CALLS -> [rebuild_banner]
def _process_states_for_end(
    states: list[MaintenanceDashboardState],
    event_id: str,
    superset_client: SupersetClient,
    db_session: Session,
) -> tuple[int, list[dict[str, Any]]]:
    removed_count = 0
    failed_removals: list[dict[str, Any]] = []

    for state in states:
        dash_id = state.dashboard_id
        app_logger.reason(
            "Processing dashboard state for end",
            extra={"dashboard_id": dash_id, "state_id": state.id},
        )

        try:
            banner_id = state.banner_id
            if not banner_id:
                # No banner linked, just mark as removed
                state.status = MaintenanceDashboardStateStatus.REMOVED
                removed_count += 1
                continue

            # Check if other active events still use this banner
            other_active = db_session.query(MaintenanceDashboardState).filter(
                MaintenanceDashboardState.banner_id == banner_id,
                MaintenanceDashboardState.status == MaintenanceDashboardStateStatus.ACTIVE,
                MaintenanceDashboardState.event_id != event_id,
            ).count()

            banner = db_session.query(MaintenanceDashboardBanner).filter(
                MaintenanceDashboardBanner.id == banner_id
            ).first()

            if other_active > 0 and banner:
                # Other events still active — rebuild banner text
                state.status = MaintenanceDashboardStateStatus.REMOVED
                db_session.flush()
                rebuild_banner(banner_id, db_session, superset_client)
                app_logger.reflect(
                    "Banner rebuilt after removing event",
                    extra={"banner_id": banner_id, "dashboard_id": dash_id},
                )
            elif banner:
                # No other events — remove banner entirely
                state.status = MaintenanceDashboardStateStatus.REMOVING
                db_session.flush()

                # Remove chart from layout
                if banner.chart_id:
                    try:
                        superset_client.remove_chart_from_layout(
                            dash_id, banner.chart_id
                        )
                    except Exception as e:
                        app_logger.explore(
                            "Failed to remove chart from layout",
                            extra={
                                "dashboard_id": dash_id,
                                "chart_id": banner.chart_id,
                            },
                            error=str(e),
                        )
                        state.status = MaintenanceDashboardStateStatus.REMOVAL_FAILED
                        failed_removals.append(
                            {"id": dash_id, "title": str(dash_id), "error": str(e)}
                        )
                        continue

                    # Delete chart
                    try:
                        superset_client.delete_chart(banner.chart_id)
                    except Exception as e:
                        app_logger.explore(
                            "Failed to delete chart",
                            extra={
                                "chart_id": banner.chart_id,
                                "dashboard_id": dash_id,
                            },
                            error=str(e),
                        )
                        state.status = MaintenanceDashboardStateStatus.REMOVAL_FAILED
                        failed_removals.append(
                            {"id": dash_id, "title": str(dash_id), "error": str(e)}
                        )
                        continue

                # Mark banner as removed
                banner.status = MaintenanceDashboardBannerStatus.REMOVED
                state.status = MaintenanceDashboardStateStatus.REMOVED
                app_logger.reflect(
                    "Banner fully removed",
                    extra={"banner_id": banner_id, "dashboard_id": dash_id},
                )
            else:
                state.status = MaintenanceDashboardStateStatus.REMOVED

            removed_count += 1

        except Exception as e:
            app_logger.explore(
                "Failed to end maintenance for dashboard",
                extra={"dashboard_id": dash_id},
                error=str(e),
            )
            state.status = MaintenanceDashboardStateStatus.REMOVAL_FAILED
            failed_removals.append(
                {"id": dash_id, "title": str(dash_id), "error": str(e)}
            )

    return removed_count, failed_removals
# #endregion _process_states_for_end

# #endregion MaintenanceChartManager
