"""The two role dashboards of the FleetSecure GRC app — the portal landing
screen of each user type:

- "GRC Platform Overview" for the GRC Tenant Admin (Core): tenants,
  packages, frameworks, controls and KPIs.
- "Compliance Overview" for the Implementation Team (Tenant): audits, audit
  tasks, KPI results, SOAs and fleet.

Each has value filters (status, type, scope …) wired to the widgets they
narrow, plus date filters, and is the landing page of its role.
"""

from .build import ROLE_CORE, ROLE_TENANT

PALETTE = ["#2563eb", "#0ea5e9", "#10b981", "#f59e0b", "#ef4444", "#8b5cf6"]


class Dashboards:
    def __init__(self, build, pages):
        self.b = build
        self.pages = pages
        self.ids: dict[str, str] = {}

    def src(self, key: str) -> str:
        return self.pages.ds(key)

    # ---- widget helpers -------------------------------------------------------------

    @staticmethod
    def kpi(key, title, *, subtitle=None, filters=None, icon=None, value=None, color="#2563eb", target=None):
        cfg = {"title": title, "subtitle": subtitle, "filters": filters or [], "icon": icon, "col_span": 1, "height": "compact"}
        if value:
            cfg["value_field"], cfg["value_aggregate"] = value
        if target is not None:
            cfg["target"] = target
        return ("kpi_card", key, color, cfg)

    @staticmethod
    def chart(type_, key, title, *, x=None, slice_=None, series=None, subtitle=None, span=3, height="normal", filters=None, **extra):
        cfg = {"title": title, "subtitle": subtitle, "col_span": span, "height": height, "filters": filters or [], "show_legend": True, **extra}
        if x:
            cfg["x_field"] = x
        if slice_:
            cfg["slice_field"] = slice_
        if series:
            cfg["series"] = series
        return (type_, key, "#2563eb", cfg)

    @staticmethod
    def values_filter(key, title, field, options, *, subtitle=None, span=2):
        return ("filter", key, "#64748b", {"title": title, "subtitle": subtitle, "filter_kind": "values", "target_field": field, "filter_options": options, "col_span": span, "height": "compact", "_targets": True})

    @staticmethod
    def date_filter(key, title, field, *, subtitle=None, span=2, mode="all_time"):
        return ("filter", key, "#64748b", {"title": title, "subtitle": subtitle, "filter_kind": "date", "filter_mode": mode, "target_field": field, "col_span": span, "height": "compact", "_targets": True})

    # ---- saving -----------------------------------------------------------------------

    def save(self, name: str, role_name: str, widgets: list[tuple], targets: dict[int, list[int]]) -> str:
        """widgets: (type, source key, color, config); targets: filter index ->
        the indexes of the widgets it narrows."""
        api = self.b.api
        existing = {d["name"]: d for d in api.get("/dashboards")}
        if name in existing:
            dash = existing[name]
            for w in api.get(f"/dashboards/{dash['id']}/widgets"):
                api.delete(f"/dashboards/{dash['id']}/widgets/{w['id']}")
        else:
            dash = api.post("/dashboards", {"name": name, "parameters": []})
        created = []
        for order, (type_, key, color, cfg) in enumerate(widgets):
            cfg = {k: v for k, v in cfg.items() if not k.startswith("_") and v is not None}
            body = {"type": type_, "data_source": self.src(key), "size": "medium", "color": color, "sort_order": order, "config": cfg}
            created.append(api.post(f"/dashboards/{dash['id']}/widgets", body))
        # Wire each filter to its widgets now that they have ids.
        for f_index, target_indexes in targets.items():
            w = created[f_index]
            config = {**w["config"], "targets": [created[i]["id"] for i in target_indexes]}
            api.patch(f"/dashboards/{dash['id']}/widgets/{w['id']}", {"config": config})
        api.patch(f"/dashboards/{dash['id']}", {"status": "published"})
        api.put(f"/dashboards/{dash['id']}/roles", [self.b.roles[role_name]["id"]])
        self.b.log(f"  {name}: {len(created)} widgets")
        return dash["id"]

    # ---- the two dashboards -----------------------------------------------------------

    def build_core(self) -> str:
        K, C = self.kpi, self.chart
        count = [{"aggregate": "count", "label": "Count"}]
        widgets = [
            # 0-1 filters
            self.values_filter("client_tenant", "Tenant status", "status", ["Active", "Provisioning", "Suspended"], subtitle="Narrows the tenant widgets"),
            self.values_filter("framework", "Framework type", "type", ["Regulatory", "Industry Standard", "Internal"], subtitle="Narrows the framework widgets"),
            self.date_filter("client_tenant", "Subscription start", "subscription_start", subtitle="Tenants subscribed in this period"),
            # 3-8 KPIs
            K("client_tenant", "Client Tenants", subtitle="All onboarded tenants", icon="Briefcase", color=PALETTE[0]),
            K("client_tenant", "Licensed Users", subtitle="Across all tenants", icon="Users", value=("licensed_users", "sum"), color=PALETTE[1]),
            K("framework", "Active Frameworks", filters=[{"field": "status", "operator": "equals", "value": "Active"}], icon="CheckCircle", color=PALETTE[2]),
            K("control", "Published Controls", filters=[{"field": "status", "operator": "equals", "value": "Published"}], icon="Layers", color=PALETTE[3]),
            K("kpi", "Active KPIs", filters=[{"field": "status", "operator": "equals", "value": "Active"}], icon="Target", color=PALETTE[5]),
            K("package", "Active Packages", filters=[{"field": "status", "operator": "equals", "value": "Active"}], icon="Package", color=PALETTE[4]),
            # 9-12 tenant charts
            C("pie_chart", "client_tenant", "Tenants by status", slice_="status", span=2, donut=True),
            C("bar_chart", "client_tenant", "Tenants by package", x="package.package_name", series=count, span=2, sort="value_desc", show_legend=False),
            C("pie_chart", "client_tenant", "Deployment model", slice_="deployment", span=2, donut=True),
            C("leaderboard", "client_tenant", "Licensed users by tenant", slice_="display_name", span=3, value_field="licensed_users", value_aggregate="sum", top_n=10),
            # 13-15 framework / control charts
            C("bar_chart", "framework", "Frameworks by category", x="category", series=count, span=3, orientation="horizontal", sort="value_desc", show_legend=False),
            C("bar_chart", "framework", "Objectives per framework", x="framework_code", series=[{"field": "objectives", "aggregate": "sum", "label": "Objectives"}, {"field": "domains", "aggregate": "sum", "label": "Domains"}], span=3),
            C("pie_chart", "framework", "Frameworks by type", slice_="type", span=2, donut=True),
            # 16-19 library charts
            C("bar_chart", "control", "Controls by category", x="category.category_name", series=count, span=2, sort="value_desc", show_legend=False),
            C("pie_chart", "control", "Controls by status", slice_="status", span=2, donut=True),
            C("bar_chart", "kpi", "KPIs by result type", x="result_type.result_type_name", series=count, span=2, orientation="horizontal", sort="value_desc", show_legend=False),
            C("bar_chart", "package", "Package pricing", x="package_name", series=[{"field": "monthly_price", "aggregate": "sum", "label": "Monthly"}], span=3, sort="value_desc",
              number_format={"style": "currency", "currency": "USD", "compact": True}, show_legend=False),
            # 20 industries, 21 table
            C("bar_chart", "industry", "Industry adoption", x="industry_code", series=[{"field": "tenants", "aggregate": "sum", "label": "Tenants"}, {"field": "frameworks_tagged", "aggregate": "sum", "label": "Frameworks"}], span=3),
            ("table", "client_tenant", "#2563eb", {"title": "Tenants", "subtitle": "Latest onboarding records", "columns": ["display_name", "tenant_code", "deployment", "region", "status"], "col_span": 6, "height": "tall"}),
        ]
        tenant_widgets = [3, 4, 9, 10, 11, 12, 21]
        return self.save("GRC Platform Overview", ROLE_CORE, widgets, {0: tenant_widgets, 1: [5, 13, 14, 15], 2: tenant_widgets})

    def build_tenant(self) -> str:
        K, C = self.kpi, self.chart
        count = [{"aggregate": "count", "label": "Count"}]
        widgets = [
            # 0-3 filters
            self.values_filter("audit_plan", "Audit type", "audit_type", ["Internal Audit", "External Audit", "Shareholder Audit", "Regulator Audit", "Review Activity"], subtitle="Narrows the audit widgets", span=3),
            self.values_filter("kpi_result", "KPI scope", "scope", ["Organisational", "Unit level"], subtitle="Narrows the KPI result widgets", span=1),
            self.values_filter("audit_task", "Task priority", "priority", ["High", "Medium", "Low"], subtitle="Narrows the audit task widgets", span=1),
            self.date_filter("audit_plan", "Audit period", "start_date", subtitle="Audits starting in this period", span=1),
            # 4-9 KPIs
            K("audit_plan", "Audits in Progress", filters=[{"field": "status", "operator": "equals", "value": "In Progress"}], icon="FileText", color=PALETTE[0]),
            K("audit_task", "Open Audit Tasks", filters=[{"field": "status", "operator": "in", "value": ["Open", "In Progress", "Rework"]}], icon="Flag", color=PALETTE[3]),
            K("kpi_result", "Overdue KPI Results", filters=[{"field": "entry_status", "operator": "equals", "value": "Overdue"}], icon="AlertCircle", color=PALETTE[4]),
            K("kpi_result", "KPI Results Up to Date", filters=[{"field": "entry_status", "operator": "equals", "value": "Up to Date"}], icon="CheckCircle", color=PALETTE[2]),
            K("soa", "Published SOAs", filters=[{"field": "status", "operator": "equals", "value": "Published"}], icon="FileText", color=PALETTE[5]),
            K("unit", "Active Fleet Units", filters=[{"field": "status", "operator": "equals", "value": "Active"}], icon="MapPin", color=PALETTE[1]),
            # 10-13 audits
            C("pie_chart", "audit_plan", "Audits by status", slice_="status", span=2, donut=True),
            C("bar_chart", "audit_plan", "Audits by type", x="audit_type", series=count, span=2, sort="value_desc", show_legend=False),
            C("line_chart", "audit_plan", "Audit schedule", x="start_date", x_bucket="month", series=count, span=2, subtitle="Audits starting per month", show_legend=False),
            C("leaderboard", "audit_plan", "Audits per lead auditor", slice_="lead_auditor.full_name", span=2, top_n=10),
            # 14-16 audit tasks / assessments
            C("bar_chart", "audit_task", "Audit tasks by status", x="status", series=count, span=2, sort="value_desc", show_legend=False),
            C("funnel_chart", "audit_task", "Audit tasks by assignee", slice_="assignee.full_name", span=2),
            C("pie_chart", "audit_assessment", "Assessment results", slice_="result", span=2, donut=True),
            # 17-19 KPI results
            C("bar_chart", "kpi_result", "KPI results by entry status", x="entry_status", series=count, span=2, sort="value_desc", show_legend=False),
            C("pie_chart", "kpi_result", "KPI results by outcome", slice_="outcome", span=2, donut=True),
            C("leaderboard", "kpi_result", "Results entered by user", slice_="assignee.full_name", span=2, top_n=10),
            # 20-22 SOA and fleet
            C("bar_chart", "soa", "SOAs by scope", x="scope", series=count, span=2, show_legend=False),
            C("pie_chart", "soa", "SOAs by framework", slice_="framework.framework_code", span=2, donut=True),
            C("pie_chart", "unit", "Fleet units by status", slice_="status", span=2, donut=True),
            # 23-24 tables
            ("table", "kpi_result", "#ef4444", {"title": "KPI results needing attention", "subtitle": "Overdue and due soon", "columns": ["kpi", "assignee", "period_label", "entry_status", "next_entry_date"],
                                               "filters": [{"field": "entry_status", "operator": "in", "value": ["Overdue", "Due Soon"]}], "col_span": 3, "height": "tall"}),
            ("table", "audit_task", "#f59e0b", {"title": "Open audit tasks", "subtitle": "Not yet submitted", "columns": ["task_title", "assignee", "due_date", "priority", "status"],
                                               "filters": [{"field": "status", "operator": "in", "value": ["Open", "In Progress", "Rework"]}], "col_span": 3, "height": "tall"}),
        ]
        audits = [4, 10, 11, 12, 13]
        return self.save("Compliance Overview", ROLE_TENANT, widgets, {0: audits, 1: [6, 7, 17, 18, 19, 23], 2: [5, 14, 15, 24], 3: audits})

    # ---- menus and landing ----------------------------------------------------------

    def publish(self) -> None:
        """Each dashboard opens its role's menu and is where its users land."""
        api = self.b.api
        core, tenant = self.build_core(), self.build_tenant()
        existing = {(m["label"], m.get("target_id")) for m in api.get("/menus", limit=500)}
        for label, dash_id in (("GRC Overview", core), ("Compliance Overview", tenant)):
            if (label, dash_id) not in existing:
                api.post("/menus", {"label": label, "icon": "bar-chart", "link_type": "existing_feature", "target_type": "dashboard", "target_id": dash_id,
                                    "group_label": None, "sort_order": 1, "status": "active", "audience": "vendor"})
        for role_name, dash_id in ((ROLE_CORE, core), (ROLE_TENANT, tenant)):
            role = next(r for r in api.get("/roles") if r["name"] == role_name)
            matrix = {**role["action_matrix"], "dashboard": [dash_id], "default_dashboard": [dash_id]}
            api.patch(f"/roles/{role['id']}", {"action_matrix": matrix, "default_page_id": None})
            self.b.log(f"  {role_name} lands on its dashboard")
