from typing import Literal, Type

from loguru import logger
from pydantic import BaseModel

from bb_integrations_lib.gravitate.rita_api import GravitateRitaAPI
from bb_integrations_lib.gravitate.sd_api import GravitateSDAPI
from bb_integrations_lib.pipelines.steps.exporting.invoice_exports import (
    GetFreightInvoicesStep,
    ConvertFreightInvoicesStep,
    MarkInvoicesExportedStep,
)
from bb_integrations_lib.pipelines.steps.exporting.sftp_export_file_step import (
    SFTPExportFileStep,
)
from bb_integrations_lib.pipelines.steps.importing.load_file_to_dataframe_step import (
    LoadFileToDataFrameStep,
)
from bb_integrations_lib.protocols.pipelines import JobPipeline, Parser
from bb_integrations_lib.provider.ftp.client import FTPIntegrationClient
from bb_integrations_lib.secrets import SecretProvider
from bb_integrations_lib.shared.model import FileType, GetFreightInvoicesRequest


class FreightInvoiceExportConfig(BaseModel):
    status: Literal["open", "sent", "blocked", "hold"] | None = "sent"
    book_type: Literal["Cost", "Revenue"] | None = "Revenue"
    file_base_name: str = "Gravitate-Freight-Invoices"
    ftp_directory: str = "/Freight Invoice"
    mark_exported: bool = True
    site_counterparty_names: list[str] | None = None


class GenericFreightInvoiceExportPipeline(JobPipeline):
    """
    Reusable freight invoice export pipeline that fetches invoices from S&D,
    optionally filters by region, converts to CSV, and uploads via SFTP.

    Pipeline flow:
        1. get_invoices      - Fetch invoices from S&D, optionally filter by region
        2. convert_invoices  - Convert invoice data to CSV with standardized columns
        3. sftp_export       - Upload CSV to SFTP (or save locally in test mode)
        4. mark_exported     - Mark invoices as exported in S&D (skipped in test mode)
    """

    @classmethod
    async def create(
        cls,
        config_id: str,
        tenant_name: str,
        sd_client: GravitateSDAPI,
        rita_client: GravitateRitaAPI,
        ftp_client: FTPIntegrationClient,
        secret_provider: SecretProvider,
        export_config: FreightInvoiceExportConfig,
        parser: Type[Parser],
        parser_kwargs: dict | None = None,
        send_reports: bool = True,
        test_invoice_numbers: list[str] | None = None,
    ):
        is_test = test_invoice_numbers is not None
        logger.info(f"Running in {'test' if is_test else 'production'} mode")

        if test_invoice_numbers:
            freight_payload = GetFreightInvoicesRequest(
                invoice_numbers=test_invoice_numbers
            )
        else:
            freight_payload = GetFreightInvoicesRequest(
                book_type=export_config.book_type,
                exported=False,
                status=export_config.status,
            )

        steps = [
            {
                "id": "get_invoices",
                "parent_id": None,
                "step": GetFreightInvoicesStep(
                    sd_client=sd_client,
                    freight_payload=freight_payload,
                    site_counterparty_names=export_config.site_counterparty_names,
                ),
            },
            {
                "id": "convert_invoices",
                "parent_id": "get_invoices",
                "step": ConvertFreightInvoicesStep(
                    file_base_name=export_config.file_base_name,
                    parser=parser,
                    parser_kwargs=parser_kwargs,
                ),
            },
        ]

        if is_test:
            steps.append(
                {
                    "id": "sftp_export",
                    "parent_id": "convert_invoices",
                    "step": LoadFileToDataFrameStep(
                        file_type=FileType.csv,
                        save_to="samples",
                        has_header=True,
                    ),
                }
            )
        else:
            steps.append(
                {
                    "id": "sftp_export",
                    "parent_id": "convert_invoices",
                    "step": SFTPExportFileStep(
                        ftp_client=ftp_client,
                        ftp_destination_dir=export_config.ftp_directory,
                    ),
                }
            )

            if export_config.mark_exported:
                steps.append(
                    {
                        "id": "mark_exported",
                        "parent_id": "sftp_export",
                        "step": MarkInvoicesExportedStep(
                            sd_client=sd_client,
                        ),
                    }
                )

        return cls(
            job_steps=steps,
            rita_client=rita_client,
            pipeline_name=f"{tenant_name} Freight Invoice Export",
            pipeline_config_id=config_id,
            secret_provider=secret_provider,
            send_reports=send_reports,
        )
