import csv
from dataclasses import dataclass
from typing import Any, Dict, List
from .process_parameters import ProcessParameters


@dataclass
class CIMDevice:
    Name: str
    Substation: str
    Connection: str
    TagMapFileName: str
    Rows: List[Dict[str | Any, str | Any]]


def process_cim_export_file(params: ProcessParameters):
    """
    Process CIM export file.
    """

    devices: Dict[str, CIMDevice] = {}

    with open(params.cim_export_file_path, newline="") as cimFile:
        filtered_lines = (line for line in cimFile if not line.startswith(params.comment_char))
        reader = csv.DictReader(filtered_lines)

        for row in reader:
            rtu_name = str(row["RTU Name"]).strip()

            if not rtu_name:
                continue

            if rtu_name not in devices:
                # Get IP address the row
                ip_address = str(row["IP address"]).strip()

                if not ip_address:
                    continue

                substation = str(row["Substation"]).strip()

                if not substation:
                    substation = rtu_name

                end_point = f"{ip_address}:{params.default_dnp3_port}"

                devices[rtu_name] = CIMDevice(
                    Name=rtu_name,
                    Substation=substation,
                    Connection=end_point,
                    TagMapFileName=f"{params.tag_map_directory}{rtu_name}-tagmap.csv",
                    Rows=[],
                )

            device = devices[rtu_name]

            # Add row to device rows
            device.Rows.append(row)

    if not devices:
        print("     WARNING: No devices found in CIM export file.")
        return

    print(f"     Found {len(devices):,} devices in CIM export file")

    # Create device CSV file with header line "Name,Connection,TagMapFileName"
    with open(params.device_csv_file_path, mode="w", newline="") as csvfile:
        fieldnames = ["Name", "Substation", "Connection", "TagMapFileName"]
        writer = csv.DictWriter(csvfile, fieldnames=fieldnames)
        writer.writeheader()

        for _, device in devices.items():
            # Write device row to CSV file
            writer.writerow(
                {
                    "Name": device.Name,
                    "Substation": device.Substation,
                    "Connection": device.Connection,
                    "TagMapFileName": device.TagMapFileName,
                }
            )

    count = 0

    # Create a tag map file for each device
    for _, device in devices.items():
        # Create tag map CSV file with header line "MapType,DNPIndex,TagName,Scale"
        with open(device.TagMapFileName, mode="w", newline="") as tag_map_file:
            fieldnames = [
                "MapType",
                "DNPIndex",
                "TagName",
                "AlternateTag",
                "Scale",
                "Negate",
                "Concatenate",
                "InvertSBit",
                "InvertXBit",
            ]
            writer = csv.DictWriter(tag_map_file, fieldnames=fieldnames)
            writer.writeheader()

            for row in device.Rows:
                telemetry_type = str(row["Telemetry Type"]).strip().lower()
                if telemetry_type == "analog":
                    map_type = "analog"
                elif telemetry_type == "discrete":
                    map_type = "doubleBitBinary"
                else:
                    continue  # Skip unsupported telemetry types

                scale = str(row.get("Scale", "1.0")).strip()

                if len(scale) == 0:
                    scale = "1.0"

                negate = str(row.get("Negate", "FALSE")).strip()

                if len(negate) == 0:
                    negate = "FALSE"

                concatenate = str(row.get("Concatenate", "FALSE")).strip()

                if len(concatenate) == 0:
                    concatenate = "FALSE"

                invert_sbit = str(row.get("InvertSBit", "FALSE")).strip()

                if len(invert_sbit) == 0:
                    invert_sbit = "FALSE"

                invert_xbit = str(row.get("InvertXBit", "FALSE")).strip()

                if len(invert_xbit) == 0:
                    invert_xbit = "FALSE"

                # Note: "Ouput" is not a typo in the source file, it's spelled that way in the CIM export file
                writer.writerow(
                    {
                        "MapType": map_type,
                        "DNPIndex": str(row["PhysicalAddress"]).strip(),
                        "TagName": str(row["Ouput name Slow"]).strip(),
                        "AlternateTag": str(row["Telemetry SCADA CompositeID"]).strip(),
                        "Scale": scale,
                        "Negate": negate,
                        "Concatenate": concatenate,
                        "InvertSBit": invert_sbit,
                        "InvertXBit": invert_xbit,
                    }
                )

        count += 1

        if count % 25 == 0:
            print(f"     Processed {count:,} devices...")

    print(f'     Processed {count:,} devices and created tag map files in "{params.tag_map_directory}" directory')
