import json
from typing import override

from bb_integrations_lib.gravitate.sd_api import GravitateSDAPI
from bb_integrations_lib.models.pipeline_structs import NoPipelineData
from bb_integrations_lib.protocols.pipelines import Step
from bb_integrations_lib.shared.model import GetFreightInvoicesRequest
from loguru import logger


class GetFreightInvoicesStep(Step):
    """Fetch freight invoices from S&D, optionally filter by region."""

    @override
    def __init__(
        self,
        sd_client: GravitateSDAPI,
        freight_payload: GetFreightInvoicesRequest,
        site_counterparty_names: list[str] | None = None,
        *args,
        **kwargs,
    ):
        super().__init__(*args, **kwargs)
        self.sd_client = sd_client
        self.freight_payload = freight_payload
        self.site_counterparty_names = site_counterparty_names

    def describe(self) -> str:
        return "Fetch freight invoices from S&D"

    async def execute(self, i: None) -> list[dict]:
        invoices = await self.get_invoices()
        if not invoices:
            raise NoPipelineData("No freight invoices to export")

        if self.site_counterparty_names:
            invoices = self.filter_by_region(invoices)
            if not invoices:
                raise NoPipelineData("No freight invoices for this region")

        logger.info(f"Fetched {len(invoices)} freight invoices")
        return invoices

    async def get_invoices(self) -> list[dict]:
        try:
            payload = self.freight_payload.model_dump(mode="json")
            resp = await self.sd_client.get_freight_invoices(self.freight_payload)
            resp.raise_for_status()
            self.pipeline_context.included_files["freight payload"] = json.dumps(
                payload
            )
            return resp.json()
        except Exception as e:
            logger.error(f"Failed to fetch freight invoices: {e}")
            raise NoPipelineData(str(e))

    def filter_by_region(self, invoices: list[dict]) -> list[dict]:
        """Filter invoices to only those with destinations in the configured region sites."""
        filtered = []
        for invoice in invoices:
            transactions = invoice.get("transactions", [])
            region_transactions = []
            for txn in transactions:
                details = txn.get("details", [])
                region_details = [
                    d
                    for d in details
                    if d.get("destination_name") in self.site_counterparty_names
                ]
                if region_details:
                    txn_copy = {**txn, "details": region_details}
                    region_transactions.append(txn_copy)
            if region_transactions:
                invoice_copy = {**invoice, "transactions": region_transactions}
                filtered.append(invoice_copy)
        return filtered
