from __future__ import annotations

from importlib import import_module
import json
from dataclasses import asdict
from pathlib import Path
from typing import Any

from src.busopt.config import ModelConfig


def _load_problem_data(config: ModelConfig):
    from src.io_utils.input_data_utils import read_mandl_data

    _, hypergraph, _, line_list, demand_list = read_mandl_data(config.candidate_routes, config)
    return hypergraph, line_list, demand_list


def _run_model(config: ModelConfig, module_name: str) -> dict[str, Any]:
    outer_relaxed_mp = import_module(module_name).Outer_relaxed_mp
    hypergraph, line_list, demand_list = _load_problem_data(config)
    outer_model = outer_relaxed_mp(hypergraph, line_list, demand_list, config)
    outer_model.solve()
    result = dict(outer_model.output_info)
    result["config"] = asdict(config)
    return result


def run_nested_gbd(config: ModelConfig) -> dict[str, Any]:
    return _run_model(config, "src.algos.nested_GBD")


def run_nested_gbd_no_operating_cost(config: ModelConfig) -> dict[str, Any]:
    nested_gbd = import_module("src.algos.nested_GBD")
    hypergraph, line_list, demand_list = _load_problem_data(config)
    outer_model = nested_gbd.Outer_relaxed_mp(
        hypergraph,
        line_list,
        demand_list,
        config,
        include_operating_cost=False,
    )
    outer_model.solve()
    result = dict(outer_model.output_info)
    result["config"] = asdict(config)
    return result


def run_nested_gbd_hpr_semi(config: ModelConfig) -> dict[str, Any]:
    return _run_model(config, "src.algos.nested_GBD_HPR_semi")


def run_nested_gbd_hpr(config: ModelConfig) -> dict[str, Any]:
    return _run_model(config, "src.algos.nested_GBD_HPR")


def run_nested_gbd_with_fixed_upper_solution(
    config: ModelConfig,
    fixed_upper_solution: dict[str, Any],
    *,
    enforce_min_freq_when_selected: bool = True,
    require_positive_profit: bool = True,
) -> dict[str, Any]:
    solve_with_fixed_upper_solution = import_module("src.algos.nested_GBD").solve_with_fixed_upper_solution
    hypergraph, line_list, demand_list = _load_problem_data(config)
    result = solve_with_fixed_upper_solution(
        hypergraph,
        line_list,
        demand_list,
        config,
        fixed_upper_solution,
        enforce_min_freq_when_selected=enforce_min_freq_when_selected,
        require_positive_profit=require_positive_profit,
    )
    result["config"] = asdict(config)
    return result


def run_nested_gbd_with_fixed_solution(
    config: ModelConfig,
    fixed_upper_solution: dict[str, Any],
    fixed_mid_solution: dict[str, Any],
) -> dict[str, Any]:
    evaluate_fixed_solution = import_module("src.algos.nested_GBD").evaluate_fixed_solution
    hypergraph, line_list, demand_list = _load_problem_data(config)
    result = evaluate_fixed_solution(
        hypergraph,
        line_list,
        demand_list,
        config,
        fixed_upper_solution,
        fixed_mid_solution,
    )
    result["config"] = asdict(config)
    return result


def run_public_operation(config: ModelConfig) -> dict[str, Any]:
    hypergraph, line_list, demand_list = _load_problem_data(config)
    fixed_upper_solution = {line.line_id: 0.0 for line in line_list}
    solve_with_fixed_upper_solution = import_module("src.algos.nested_GBD").solve_with_fixed_upper_solution
    result = solve_with_fixed_upper_solution(
        hypergraph,
        line_list,
        demand_list,
        config,
        fixed_upper_solution,
        require_positive_profit=False,
    )
    result["config"] = asdict(config)
    result["regime"] = "public_operation"
    return result


def run_open_access(config: ModelConfig) -> dict[str, Any]:
    hypergraph, line_list, demand_list = _load_problem_data(config)
    fixed_upper_solution = {line.line_id: 1.0 for line in line_list}
    solve_with_fixed_upper_solution = import_module("src.algos.nested_GBD").solve_with_fixed_upper_solution
    result = solve_with_fixed_upper_solution(
        hypergraph,
        line_list,
        demand_list,
        config,
        fixed_upper_solution,
        enforce_min_freq_when_selected=False,
        require_positive_profit=False,
    )
    result["config"] = asdict(config)
    result["regime"] = "open_access"
    return result


def save_result(result: dict[str, Any], output_path: Path) -> None:
    output_path.parent.mkdir(parents=True, exist_ok=True)
    with output_path.open("w", encoding="utf-8") as file_obj:
        json.dump(result, file_obj, indent=2)


if __name__ == '__main__':
    run_nested_gbd(ModelConfig())
