import json
import os
import pathlib
import subprocess
from typing import TYPE_CHECKING, Optional

import numpy as np

if TYPE_CHECKING:
    # Include only when type checking, as it requires Qt
    from CityForge.toolbelt.ProgressEmitter import ProgressEmitter


# class called for the plugin executor and line executor
class JsonConstructor:
    def __init__(self):
        pass

    def reconstruct(
        self,
        footprint: pathlib.Path,
        pointcloud: pathlib.Path,
        output_directory: pathlib.Path,
        progress_emitter: Optional["ProgressEmitter"],
        crs: str,
        geoflow_install: str,
        docker_path: str,
        executable_path: str,
        obj: bool,
        gpkg: bool,
        debug: bool = False,
    ) -> subprocess.CompletedProcess[str]:
        """
        Args:
            footprint (pathlib.Path): polygon file path
            pointcloud (pathlib.Path): point cloud file path
            output_directory (pathlib.Path): output directory path
            progress_emitter

        Returns:
            Command docker build
        """
        cmd = None
        if progress_emitter:
            current_step = 50
            self.calculateProgress(current_step, progress_emitter)

        crs = f"https://www.opengis.net/def/crs/EPSG/0/{crs}"

        if geoflow_install == "docker":
            args = [
                docker_path,
                "run",
                "--rm",
                "--network=host",
                "-v",
                f"{footprint.parent}:/fp",
                "-v",
                f"{pointcloud.parent}:/pc",
                "-v",
                f"{output_directory}:/out",
                "ignfab/lod22-reconstruct-batch:latest",
                f"--input_footprint=/fp/{footprint.name}",
                f"--input_pointcloud=/pc/{pointcloud.name}",
                "--output_cityjson=/out/model.json",
                f"--output_cj_referenceSystem={crs}",
            ]

            if obj:
                args += ["--output_obj_lod12=/out/model_lod12.obj"]
                args += ["--output_obj_lod13=/out/model_lod13.obj"]
                args += ["--output_obj_lod22=/out/model_lod22.obj"]

            if gpkg:
                args += ["--output_ogr=/out/model_2d.gpkg"]

            if debug:
                args.append("-V")  # Verbose

            cmd = subprocess.run(args, capture_output=True, text=True)

        else:
            cityforge_directory = pathlib.Path(
                os.path.dirname(os.path.abspath(__file__))
            )
            geoflow_file = pathlib.Path(executable_path)
            model_file = output_directory / "model.json"
            env = os.environ.copy()
            if "GF_INSTALL_ROOT" not in env:
                # should point to something like C:\Program Files\Geoflow
                geoflow_root = geoflow_file.parent.parent
                env["GF_INSTALL_ROOT"] = str(geoflow_root)
                env["GF_PLUGIN_FOLDER"] = str(geoflow_root / "lib" / "geoflow-plugins")
            output_obj_lod22 = output_directory / "model_lod22.obj"
            output_obj_lod12 = output_directory / "model_lod12.obj"
            output_obj_lod13 = output_directory / "model_lod13.obj"
            output_ogr = output_directory / "model_2d.gpkg"
            args = [
                str(geoflow_file),
                str(cityforge_directory / "flowchart" / "reconstruct.json"),
                f"--input_footprint={footprint}",
                f"--input_pointcloud={pointcloud}",
                f"--output_cityjson={model_file}",
                f"--output_cj_referenceSystem={crs}",
            ]

            if obj:
                args += [f"--output_obj_lod12={output_obj_lod12}"]
                args += [f"--output_obj_lod13={output_obj_lod13}"]
                args += [f"--output_obj_lod22={output_obj_lod22}"]

            if gpkg:
                args += [f"--output_ogr={output_ogr}"]

            if debug:
                args.append("-V")  # Verbose

            cmd = subprocess.run(args, capture_output=True, text=True, env=env)

        if progress_emitter:
            current_step = 75
            self.calculateProgress(current_step, progress_emitter)
        return cmd

    def fixup(
        self,
        output_directory: pathlib.Path,
        lod: bool,
        progress_emitter: Optional["ProgressEmitter"],
    ) -> Optional[pathlib.Path]:
        """
        Args:
            output_directory (pathlib.Path):output directory path
            lod (boolean) : True for generate only LOD 2.2
            progress_emitter (_type_):

        Returns:
            json file path
        """
        # Loading JSON file and update the reference system
        json_file = output_directory / "model.json"
        if not json_file.exists():
            return None
        with open(json_file, "r") as f:
            data = json.load(f)

        # Deleting LOD 0.0, 1.2, 1.3
        city_objects = data["CityObjects"]
        for object_data in city_objects.items():
            data_dict = object_data[1]
            geometry = data_dict.get("geometry", [])
            if lod:
                for g in geometry:
                    if g.get("lod") != "2.2":
                        g["boundaries"] = []

        with open(json_file, "w") as f:
            json.dump(
                data,
                f,
                indent=4,
                ensure_ascii=False,
                default=lambda o: o.tolist() if isinstance(o, np.ndarray) else o,
            )

        current_step = 90
        self.calculateProgress(current_step, progress_emitter)

        return json_file

    def calculateProgress(
        self, current_step: int, progress_emitter: Optional["ProgressEmitter"]
    ) -> None:
        if progress_emitter:
            progress_emitter.progress_updated.emit(current_step)


if __name__ == "__main__":

    constructor = JsonConstructor()
