"""
export_CAx_from_tia : Script que exporta los datos CAx de un proyecto de TIA Portal y genera un resumen en Markdown.
"""

import tkinter as tk
from tkinter import filedialog
import os
import sys
import traceback
import xml.etree.ElementTree as ET  # Library to parse XML (AML)
from pathlib import Path  # Import Path

script_root = os.path.dirname(
    os.path.dirname(os.path.dirname(os.path.dirname(__file__)))
)
sys.path.append(script_root)
from backend.script_utils import load_configuration

# --- Configuration ---
# Supported TIA Portal versions mapping (extension -> version)
SUPPORTED_TIA_VERSIONS = {
    ".ap18": "18.0",
    ".ap19": "19.0", 
    ".ap20": "20.0"
}

# --- TIA Scripting Import Handling ---
# (Same import handling as the previous script)
if os.getenv("TIA_SCRIPTING"):
    sys.path.append(os.getenv("TIA_SCRIPTING"))
else:
    pass

try:
    import siemens_tia_scripting as ts
except ImportError:
    print("ERROR: Failed to import 'siemens_tia_scripting'.")
    print("Ensure TIA Openness, the module, and Python 3.12.X are set up.")
    sys.exit(1)
except Exception as e:
    print(f"An unexpected error occurred during import: {e}")
    traceback.print_exc()
    sys.exit(1)

# --- Functions ---


def get_supported_filetypes():
    """Returns the supported file types for TIA Portal projects."""
    filetypes = []
    for ext, version in SUPPORTED_TIA_VERSIONS.items():
        version_major = version.split('.')[0]
        filetypes.append((f"TIA Portal V{version_major} Projects", f"*{ext}"))
    
    # Add option to show all supported files
    all_extensions = " ".join([f"*{ext}" for ext in SUPPORTED_TIA_VERSIONS.keys()])
    filetypes.insert(0, ("All TIA Portal Projects", all_extensions))
    
    return filetypes


def detect_tia_version(project_file_path):
    """Detects TIA Portal version based on file extension."""
    file_path = Path(project_file_path)
    file_extension = file_path.suffix.lower()
    
    if file_extension in SUPPORTED_TIA_VERSIONS:
        detected_version = SUPPORTED_TIA_VERSIONS[file_extension]
        print(f"Detected TIA Portal version: {detected_version} (from extension {file_extension})")
        return detected_version
    else:
        print(f"WARNING: Unrecognized file extension '{file_extension}'. Supported extensions: {list(SUPPORTED_TIA_VERSIONS.keys())}")
        # Default to version 18.0 for backward compatibility
        print("Defaulting to TIA Portal V18.0")
        return "18.0"


def select_project_file():
    """Opens a dialog to select a TIA Portal project file."""
    root = tk.Tk()
    root.withdraw()
    file_path = filedialog.askopenfilename(
        title="Select TIA Portal Project File",
        filetypes=get_supported_filetypes(),
    )
    root.destroy()
    if not file_path:
        print("No project file selected. Exiting.")
        sys.exit(0)
    return file_path


def select_output_directory():
    """Opens a dialog to select the output directory."""
    root = tk.Tk()
    root.withdraw()
    dir_path = filedialog.askdirectory(
        title="Select Output Directory for AML and MD files"
    )
    root.destroy()
    if not dir_path:
        print("No output directory selected. Exiting.")
        sys.exit(0)
    return dir_path


def find_elements(element, path):
    """Helper to find elements using namespaces commonly found in AML."""
    # AutomationML namespaces often vary slightly or might be default
    # This basic approach tries common prefixes or no prefix
    namespaces = {
        "": (
            element.tag.split("}")[0][1:] if "}" in element.tag else ""
        ),  # Default namespace if present
        "caex": "http://www.dke.de/CAEX",  # Common CAEX namespace
        # Add other potential namespaces if needed based on file inspection
    }
    # Try finding with common prefixes or the default namespace
    for prefix, uri in namespaces.items():
        # Construct path with namespace URI if prefix is defined
        namespaced_path = path
        if prefix:
            parts = path.split("/")
            namespaced_parts = [
                f"{{{uri}}}{part}" if part != "." else part for part in parts
            ]
            namespaced_path = "/".join(namespaced_parts)

        # Try findall with the constructed path
        found = element.findall(namespaced_path)
        if found:
            return found  # Return first successful find

    # Fallback: try finding without explicit namespace (might work if default ns is used throughout)
    # This might require adjusting the path string itself depending on the XML structure
    try:
        # Simple attempt without namespace handling if the above fails
        return element.findall(path)
    except (
        SyntaxError
    ):  # Handle potential errors if path isn't valid without namespaces
        return []


def parse_aml_to_markdown(aml_file_path, md_file_path):
    """Parses the AML file and generates a Markdown summary."""
    print(f"Parsing AML file: {aml_file_path}")
    try:
        tree = ET.parse(aml_file_path)
        root = tree.getroot()

        markdown_lines = ["# Project CAx Data Summary (AutomationML)", ""]

        # Find InstanceHierarchy - usually contains the project structure
        # Note: Namespace handling in ElementTree can be tricky. Adjust '{...}' part if needed.
        # We will use a helper function 'find_elements' to try common patterns
        instance_hierarchies = find_elements(
            root, ".//InstanceHierarchy"
        )  # Common CAEX tag

        if not instance_hierarchies:
            markdown_lines.append("Could not find InstanceHierarchy in the AML file.")
            print("Warning: Could not find InstanceHierarchy element.")
        else:
            # Assuming the first InstanceHierarchy is the main one
            ih = instance_hierarchies[0]
            markdown_lines.append(f"## Instance Hierarchy: {ih.get('Name', 'N/A')}")
            markdown_lines.append("")

            # Look for InternalElements which represent devices/components
            internal_elements = find_elements(
                ih, ".//InternalElement"
            )  # Common CAEX tag

            if not internal_elements:
                markdown_lines.append(
                    "No devices (InternalElement) found in InstanceHierarchy."
                )
                print("Info: No InternalElement tags found under InstanceHierarchy.")
            else:
                markdown_lines.append(
                    f"Found {len(internal_elements)} device(s)/component(s):"
                )
                markdown_lines.append("")
                markdown_lines.append(
                    "| Name | SystemUnitClass | RefBaseSystemUnitPath | Attributes |"
                )
                markdown_lines.append("|---|---|---|---|")

                for elem in internal_elements:
                    name = elem.get("Name", "N/A")
                    ref_path = elem.get(
                        "RefBaseSystemUnitPath", "N/A"
                    )  # Path to class definition

                    # Try to get the class name from the RefBaseSystemUnitPath or SystemUnitClassLib
                    su_class_path = find_elements(
                        elem, ".//SystemUnitClass"
                    )  # Check direct child first
                    su_class = (
                        su_class_path[0].get("Path", "N/A")
                        if su_class_path
                        else ref_path.split("/")[-1]
                    )  # Fallback to last part of path

                    attributes_md = ""
                    attributes = find_elements(elem, ".//Attribute")  # Find attributes
                    attr_list = []
                    for attr in attributes:
                        attr_name = attr.get("Name", "")
                        attr_value_elem = find_elements(
                            attr, ".//Value"
                        )  # Get Value element
                        attr_value = (
                            attr_value_elem[0].text
                            if attr_value_elem and attr_value_elem[0].text
                            else "N/A"
                        )

                        # Look for potential IP addresses (common attribute names)
                        if "Address" in attr_name or "IP" in attr_name:
                            attr_list.append(f"**{attr_name}**: {attr_value}")
                        else:
                            attr_list.append(f"{attr_name}: {attr_value}")

                    attributes_md = "<br>".join(attr_list) if attr_list else "None"

                    markdown_lines.append(
                        f"| {name} | {su_class} | `{ref_path}` | {attributes_md} |"
                    )

        # Write to Markdown file
        with open(md_file_path, "w", encoding="utf-8") as f:
            f.write("\n".join(markdown_lines))
        print(f"Markdown summary written to: {md_file_path}")

    except ET.ParseError as xml_err:
        print(f"ERROR parsing XML file {aml_file_path}: {xml_err}")
        with open(md_file_path, "w", encoding="utf-8") as f:
            f.write(
                f"# Error\n\nFailed to parse AML file: {os.path.basename(aml_file_path)}\n\nError: {xml_err}"
            )
    except Exception as e:
        print(f"ERROR processing AML file {aml_file_path}: {e}")
        traceback.print_exc()
        with open(md_file_path, "w", encoding="utf-8") as f:
            f.write(
                f"# Error\n\nAn unexpected error occurred while processing AML file: {os.path.basename(aml_file_path)}\n\nError: {e}"
            )


# --- Main Script ---

if __name__ == "__main__":
    configs = load_configuration()
    working_directory = configs.get("working_directory")

    print("--- TIA Portal Project CAx Exporter and Analyzer ---")

    # Validate working directory
    if not working_directory or not os.path.isdir(working_directory):
        print("ERROR: Working directory not set or invalid in configuration.")
        print("Please configure the working directory using the main application.")
        sys.exit(1)

    # 1. Select Project File, Output Directory comes from config
    project_file = select_project_file()
    output_dir = Path(
        working_directory
    )  # Use working directory from config, ensure it's a Path object

    print(f"\nSelected Project: {project_file}")
    print(f"Using Output Directory (Working Directory): {output_dir}")

    # 2. Detect TIA Portal version from project file
    tia_version = detect_tia_version(project_file)

    # Define output file names using Path object
    project_path = Path(project_file)
    project_base_name = project_path.stem  # Get filename without extension
    aml_file = output_dir / f"{project_base_name}_CAx_Export.aml"
    md_file = output_dir / f"{project_base_name}_CAx_Summary.md"
    log_file = (
        output_dir / f"{project_base_name}_CAx_Export.log"
    )  # Log file for the export process

    print(f"Will export CAx data to: {aml_file}")
    print(f"Will generate summary to: {md_file}")
    print(f"Export log file: {log_file}")

    portal_instance = None
    project_object = None
    cax_export_successful = False

    try:
        # 3. Connect to TIA Portal with detected version
        print(f"\nConnecting to TIA Portal V{tia_version}...")
        portal_instance = ts.open_portal(
            version=tia_version,
            portal_mode=ts.Enums.PortalMode.WithGraphicalUserInterface,
        )
        print("Connected.")

        # 4. Open Project
        print(
            f"Opening project: {project_path.name}..."
        )  # Use Path object's name attribute
        project_object = portal_instance.open_project(
            project_file_path=str(project_path)
        )  # Pass path as string
        if project_object is None:
            print("Project might already be open, attempting to get handle...")
            project_object = portal_instance.get_project()
            if project_object is None:
                raise Exception("Failed to open or get the specified project.")
        print("Project opened.")

        # 5. Export CAx Data (Project Level)
        print(f"Exporting CAx data for the project to {aml_file}...")
        # Ensure output directory exists (Path.mkdir handles this implicitly if needed later, but good practice)
        output_dir.mkdir(parents=True, exist_ok=True)

        # Pass paths as strings to the TIA function
        export_result = project_object.export_cax_data(
            export_file_path=str(aml_file), log_file_path=str(log_file)
        )

        if export_result:
            print("CAx data exported successfully.")
            cax_export_successful = True
        else:
            print("CAx data export failed. Check the log file for details:")
            print(f"  Log file: {log_file}")
            # Write basic error message to MD file if export fails
            with open(md_file, "w", encoding="utf-8") as f:
                f.write(
                    f"# Error\n\nCAx data export failed. Check log file: {log_file}"
                )

    except ts.TiaException as tia_ex:
        print(f"\nTIA Portal Openness Error: {tia_ex}")
        traceback.print_exc()
    except FileNotFoundError:
        print(f"\nERROR: Project file not found at {project_file}")
    except Exception as e:
        print(f"\nAn unexpected error occurred during TIA interaction: {e}")
        traceback.print_exc()
    finally:
        # Close TIA Portal before processing the file (or detach)
        if portal_instance:
            try:
                print("\nClosing TIA Portal...")
                portal_instance.close_portal()
                print("TIA Portal closed.")
            except Exception as close_ex:
                print(f"Error during TIA Portal cleanup: {close_ex}")

    # 6. Parse AML and Generate Markdown (only if export was successful)
    if cax_export_successful:
        if aml_file.exists():  # Use Path object's exists() method
            parse_aml_to_markdown(aml_file, md_file)
        else:
            print(
                f"ERROR: Export was reported successful, but AML file not found at {aml_file}"
            )
            with open(md_file, "w", encoding="utf-8") as f:
                f.write(
                    f"# Error\n\nExport was reported successful, but AML file not found:\n{aml_file}"
                )

    print("\nScript finished.")
