#### Name: Hydro Features Near WBD
#### Purpose: help identify Hydro Features that are close to the WBD boundary and may have pushed the
####          WBD boundary off the ridge due to their proximity.
#### Inputs: Workspace (assumed to be GDB); Hydro Features; WBD Lines;
####         Search Distance: Vertices Excluded Near Intersection; Search Distance: Verticies Near WBD
#### Outputs: WBDLines_HydroIntersections; WBDLines_near_HydroFeatures
#### Adapted from original script by Gardner Pierson gpierson@contractor.usgs.gov

import sys
from pathlib import Path
from typing import Dict

import arcpy

from base_classes import Tool
from utils import print_header, isolate_env


class HydroNearBoundary(Tool):
    def __init__(self):
        """Define the tool (tool name is the name of the class)."""
        self.label = "G4 Boundary Lines Near Hydro"
        self.description = (
            "Finds drainage area boundary lines near hydrographic features"
        )
        self.category = "Geometry checks"
        self.canRunInBackground = False

    def getParameterInfo(self):
        """Define parameter definitions"""
        output_gdb = arcpy.Parameter(
            displayName="Output geodatabase",
            name="output_gdb",
            datatype="DEWorkspace",
            parameterType="Required",
            direction="Input",
        )
        output_gdb.filter.list = ["Local Database"]

        da_bnd_line = arcpy.Parameter(
            displayName="Drainage area boundary lines",
            name="da_bnd_line",
            datatype="DEFeatureClass",
            parameterType="Required",
            direction="Input",
        )
        da_bnd_line.filter.list = ["Polyline"]

        flowline = arcpy.Parameter(
            displayName="Flowline features",
            name="flowline",
            datatype="DEFeatureClass",
            parameterType="Required",
            direction="Input",
        )
        flowline.filter.list = ["Polyline"]

        waterbody = arcpy.Parameter(
            displayName="Waterbody features",
            name="waterbody",
            datatype="DEFeatureClass",
            parameterType="Required",
            direction="Input",
        )
        waterbody.filter.list = ["Polygon"]

        dist_from_boundary = arcpy.Parameter(
            displayName="Search distance from hydro features",
            name="dist_from_boundary",
            datatype="GPDouble",
            parameterType="Required",
            direction="Input",
        )
        dist_from_boundary.value = 15.0

        dist_from_outlet = arcpy.Parameter(
            displayName="Exclusion radius around drainage area outlets",
            name="dist_from_outlet",
            datatype="GPDouble",
            parameterType="Required",
            direction="Input",
        )
        dist_from_outlet.value = 100.0

        return [
            output_gdb,
            da_bnd_line,
            flowline,
            waterbody,
            dist_from_boundary,
            dist_from_outlet,
        ]

    def execute(self, parameters, messages):
        print_header(self)
        lookup: Dict[str, arcpy.Parameter] = {p.name: p for p in parameters}
        main(
            Path(lookup["output_gdb"].valueAsText),
            Path(lookup["da_bnd_line"].valueAsText),
            Path(lookup["flowline"].valueAsText),
            Path(lookup["waterbody"].valueAsText),
            float(lookup["dist_from_boundary"].value),
            float(lookup["dist_from_outlet"].value),
        )


@isolate_env
def main(
    output_gdb: Path,
    da_bnd_line: Path,
    flowline: Path,
    waterbody: Path,
    dist_from_boundary: float,
    dist_from_outlet: float,
):

    # Set Up Workspace
    folder_path = output_gdb.parent
    if not arcpy.Exists(str(output_gdb)):
        arcpy.CreateFileGDB_management(str(folder_path), output_gdb.name)
    temp_folder = folder_path.joinpath("temp")
    temp_folder.mkdir(exist_ok=True)
    temp_gdb = temp_folder.joinpath("temp.gdb")
    if not arcpy.Exists(str(temp_gdb)):
        arcpy.CreateFileGDB_management(str(temp_folder), temp_gdb.name)

    arcpy.env.workspace = str(temp_gdb)
    arcpy.env.scratchWorkspace = str(temp_gdb)
    arcpy.env.overwriteOutput = True
    arcpy.env.parallelProcessingFactor = "100%"

    # Define Variables
    temp_fl = temp_gdb.joinpath("flowlines")
    temp_wb = temp_gdb.joinpath("waterbodies")
    temp_outlet_pts = temp_gdb.joinpath("outlet_points")
    temp_outlet_buffer = temp_gdb.joinpath("outlet_buffers")
    temp_fl_buffer = temp_gdb.joinpath("flowline_buffers")
    temp_wb_buffer = temp_gdb.joinpath("waterbody_buffers")
    temp_hydro_buffer = temp_gdb.joinpath("hydro_buffers")
    temp_hydro_buf_dissolve = temp_gdb.joinpath("hydro_buffers_dissolved")
    temp_bnd_clip = temp_gdb.joinpath("boundary_hydro_clip")
    temp_bnd_erase = temp_gdb.joinpath("boundary_hydro_clip_erase")
    out_da_line = output_gdb.joinpath("G4_boundary_near_hydro")

    # Project to match WBD
    arcpy.AddMessage("Projecting hydro to match boundary lines")
    wbd_sr = arcpy.Describe(str(da_bnd_line)).spatialReference
    arcpy.Project_management(str(waterbody), str(temp_wb), out_coor_system=wbd_sr)
    arcpy.Project_management(str(flowline), str(temp_fl), out_coor_system=wbd_sr)

    # Get outlet points
    arcpy.AddMessage("Intersecting flowlines with boundary lines to find outlet points")
    arcpy.Intersect_analysis(
        [str(temp_fl), str(da_bnd_line)], str(temp_outlet_pts), output_type="POINT"
    )

    # Buffer outlets
    arcpy.AddMessage("Buffering around outlet points")
    arcpy.PairwiseBuffer_analysis(
        str(temp_outlet_pts),
        str(temp_outlet_buffer),
        buffer_distance_or_field=f"{dist_from_outlet} Meters",
    )

    # Select hydro features within distance of boundaries and buffer
    arcpy.AddMessage(f"Buffering hydro features to {dist_from_boundary} meters")
    lyr_fl = arcpy.MakeFeatureLayer_management(str(temp_fl))
    arcpy.SelectLayerByLocation_management(
        lyr_fl, "WITHIN_A_DISTANCE", str(da_bnd_line), dist_from_boundary
    )
    arcpy.PairwiseBuffer_analysis(
        lyr_fl, str(temp_fl_buffer), dist_from_boundary, "ALL"
    )

    lyr_wb = arcpy.MakeFeatureLayer_management(str(temp_wb))
    arcpy.SelectLayerByLocation_management(
        lyr_wb, "WITHIN_A_DISTANCE", str(da_bnd_line), dist_from_boundary
    )
    arcpy.PairwiseBuffer_analysis(
        lyr_wb, str(temp_wb_buffer), dist_from_boundary, "ALL"
    )

    # Merge and dissolve hydro buffers
    arcpy.AddMessage(f"Merging and dissolving hydro buffers")
    arcpy.Merge_management(
        [str(temp_wb_buffer), str(temp_fl_buffer)], str(temp_hydro_buffer)
    )
    arcpy.PairwiseDissolve_analysis(
        str(temp_hydro_buffer), str(temp_hydro_buf_dissolve)
    )

    # Clip drainage area boundaries to hydro buffers
    arcpy.AddMessage(f"Clipping boundary lines to hydro buffer")
    arcpy.PairwiseClip_analysis(
        str(da_bnd_line), str(temp_hydro_buf_dissolve), str(temp_bnd_clip)
    )

    # Erase results near outlet
    arcpy.AddMessage(f"Erasing clipped boundary lines using outlet buffers")
    arcpy.PairwiseErase_analysis(
        str(temp_bnd_clip), str(temp_outlet_buffer), str(temp_bnd_erase)
    )
    arcpy.MultipartToSinglepart_management(str(temp_bnd_erase), str(out_da_line))

    arcpy.AddField_management(str(out_da_line), "Reviewed", "TEXT")


if __name__ == "__main__":
    args = sys.argv[1:]
    main(
        Path(args[0]),
        Path(args[1]),
        Path(args[2]),
        Path(args[3]),
        float(args[4]),
        float(args[5]),
    )
