"""Local entry point. Production injects private OSS and processing adapters."""

from __future__ import annotations

import argparse
from pathlib import Path
from typing import Any, Callable, Optional, Sequence

from arr_database import controlled_connect
from arr_web.app import PortalApplication, RuntimeHealth
from arr_web.auth import LoginCredentials
from arr_web.booking_uploads import ProgramBookingSourceCoordinator
from arr_web.company_jobs import (
    PersistentCompanyReportCoordinator,
    ProgramCompanyReportExecutor,
)
from arr_web.downloads import (
    ControlledProjectArtifactReader,
    ManagedObjectArtifactReader,
    RoutedArtifactReader,
)
from arr_web.repository import PostgresPortalRepository, UnavailablePortalRepository
from arr_web.processing_runtime import (
    ObjectStoreRuntime,
    ProcessingInputRuntime,
    compose_object_store,
    compose_programmatic_processing,
)
from arr_web.server import serve
from arr_web.services import ProgramMonthlyCoordinator
from monthly_reports.publishing import OpenpyxlWorkbookBuilder, AtomicReportPublisher
from monthly_reports.repository import DatabaseConfig, PostgresReportRepository
from monthly_reports.service import MonthlyReportService
from company_reports.publishing import (
    OpenpyxlWorkbookBuilder as CompanyOpenpyxlWorkbookBuilder,
    AtomicReportPublisher as AtomicCompanyReportPublisher,
)
from company_reports.repository import (
    DatabaseConfig as CompanyDatabaseConfig,
    PostgresReportRepository as PostgresCompanyReportRepository,
)
from company_reports.service import CompanyReportService
from booking_ingestion.excel_postgres import (
    DatabaseConfig as BookingExcelDatabaseConfig,
)
from booking_ingestion.excel_review_postgres import PostgresBookingReviewRepository


PROJECT_ROOT = Path(__file__).resolve().parents[1]


def _parser() -> argparse.ArgumentParser:
    parser = argparse.ArgumentParser(prog="arr-web")
    parser.add_argument("--host", default="127.0.0.1")
    parser.add_argument("--port", type=int, default=8765)
    parser.add_argument(
        "--db-config",
        type=Path,
        help="local controlled ARR_DB_* env file; values are never logged",
    )
    parser.add_argument(
        "--driver-path",
        type=Path,
        help="optional local directory containing the PostgreSQL driver",
    )
    parser.add_argument(
        "--enable-monthly-generation",
        action="store_true",
        help="enable the database monthly-report mutation endpoint",
    )
    parser.add_argument(
        "--enable-company-reports",
        action="store_true",
        help="enable Bangkok-gated five-company channel-detail jobs",
    )
    parser.add_argument(
        "--enable-processing",
        action="store_true",
        help="enable ARR-owned deterministic XML processing and ingestion",
    )
    parser.add_argument(
        "--secure-cookies",
        action="store_true",
        help="mark browser session cookies Secure for an HTTPS deployment",
    )
    return parser


def main(argv: Optional[Sequence[str]] = None) -> int:
    args = _parser().parse_args(argv)
    try:
        login_credentials = LoginCredentials.from_environment()
    except ValueError as error:
        raise SystemExit(str(error)) from None
    connect: Optional[Callable[[str], Any]] = None
    try:
        if args.db_config:
            connect = controlled_connect(args.db_config, args.driver_path)
            repository = PostgresPortalRepository(
                "controlled",
                connect=connect,
            )
        else:
            repository = PostgresPortalRepository.from_environment()
        repository.list_months()
        database_ready = True
    except Exception:
        repository = UnavailablePortalRepository()
        database_ready = False

    processing_input: Optional[ProcessingInputRuntime] = None
    processing_ready = False
    if args.enable_processing and database_ready:
        try:
            processing_input = compose_programmatic_processing(
                project_root=PROJECT_ROOT,
                connect=connect,
            )
            processing_ready = True
        except Exception:
            processing_input = None
            processing_ready = False

    report_storage_runtime: Optional[ObjectStoreRuntime] = None
    report_object_store = (
        processing_input.object_store if processing_input is not None else None
    )
    if report_object_store is None and database_ready and (
        args.enable_monthly_generation or args.enable_company_reports
    ):
        try:
            report_storage_runtime = compose_object_store()
            report_object_store = report_storage_runtime.object_store
        except Exception:
            report_storage_runtime = None
            report_object_store = None

    monthly = None
    monthly_ready = False
    if args.enable_monthly_generation and database_ready and report_object_store is not None:
        try:
            report_repository = PostgresReportRepository(
                DatabaseConfig("controlled"),
                connect=connect,
            ) if connect is not None else PostgresReportRepository(DatabaseConfig.from_environment())
            output_root = PROJECT_ROOT / "outputs" / "monthly_reports"
            monthly = ProgramMonthlyCoordinator(
                MonthlyReportService(
                    report_repository,
                    OpenpyxlWorkbookBuilder(),
                    AtomicReportPublisher(
                        PROJECT_ROOT,
                        output_root,
                        object_store=report_object_store,
                    ),
                    output_root / ".staging",
                )
            )
            monthly_ready = True
        except Exception:
            monthly = None
            monthly_ready = False
    company_reports = None
    company_reports_ready = False
    if args.enable_company_reports and database_ready and report_object_store is not None:
        try:
            company_repository = (
                PostgresCompanyReportRepository(
                    CompanyDatabaseConfig("controlled"),
                    connect=connect,
                )
                if connect is not None
                else PostgresCompanyReportRepository(
                    CompanyDatabaseConfig.from_environment()
                )
            )
            company_output_root = PROJECT_ROOT / "outputs" / "company_reports"
            company_service = CompanyReportService(
                company_repository,
                CompanyOpenpyxlWorkbookBuilder(),
                AtomicCompanyReportPublisher(
                    PROJECT_ROOT,
                    company_output_root,
                    object_store=report_object_store,
                ),
                company_output_root / ".staging",
            )
            company_reports = PersistentCompanyReportCoordinator(
                PROJECT_ROOT,
                company_output_root,
                ProgramCompanyReportExecutor(company_service),
                object_store=report_object_store,
            )
            company_reports_ready = True
        except Exception:
            company_reports = None
            company_reports_ready = False
    booking_sources = None
    company_source_upload_ready = False
    if args.enable_company_reports and database_ready and processing_input is not None:
        try:
            booking_repository = PostgresBookingReviewRepository(
                BookingExcelDatabaseConfig("controlled"),
                connect=connect,
            ) if connect is not None else PostgresBookingReviewRepository(
                BookingExcelDatabaseConfig.from_environment()
            )
            booking_sources = ProgramBookingSourceCoordinator(
                processing_input.object_store,
                booking_repository,
            )
            booking_sources.current()
            booking_sources.draft()
            company_source_upload_ready = True
        except Exception:
            booking_sources = None
            company_source_upload_ready = False
    application = PortalApplication(
        repository=repository,
        upload=(
            processing_input.coordinator if processing_input is not None else None
        ),
        price_reviews=(
            processing_input.coordinator if processing_input is not None else None
        ),
        monthly=monthly,
        company_reports=company_reports,
        booking_sources=booking_sources,
        artifact_reader=(
            RoutedArtifactReader(
                daily_reader=(
                    ManagedObjectArtifactReader(processing_input.object_store)
                    if processing_input is not None
                    else None
                ),
                local_reader=ControlledProjectArtifactReader(PROJECT_ROOT),
                oss_reader=(
                    ManagedObjectArtifactReader(report_object_store)
                    if report_object_store is not None
                    else None
                ),
            )
            if database_ready
            else None
        ),
        health=RuntimeHealth(
            database_ready=database_ready,
            upload_ready=processing_ready,
            monthly_ready=monthly_ready,
            download_ready=database_ready,
            company_reports_ready=company_reports_ready,
            company_source_upload_ready=company_source_upload_ready,
        ),
        credentials=login_credentials,
        secure_cookies=args.secure_cookies,
    )
    try:
        serve(application, args.host, args.port)
    finally:
        if company_reports is not None:
            company_reports.close()
        if processing_input is not None:
            processing_input.close()
        if report_storage_runtime is not None:
            report_storage_runtime.close()
    return 0


if __name__ == "__main__":
    raise SystemExit(main())
