"""
export_cross_references_from_tia : Script para exportar las referencias cruzadas
de un proyecto TIA Portal a archivos (probablemente XML).
"""

import tkinter as tk
from tkinter import filedialog
import os
import sys
import traceback
from pathlib import Path  # Import Path for easier path manipulation

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 ---
TIA_PORTAL_VERSION = "18.0"  # Target TIA Portal version (e.g., "18.0")
# Filter for cross-references. Based on documentation:
# 1: 'AllObjects', 2: 'ObjectsWithReferences', 3: 'ObjectsWithoutReferences', 4: 'UnusedObjects'
# Using 1 to export all. 0 might also work as a default in some API versions.
CROSS_REF_FILTER = 1

# --- TIA Scripting Import Handling ---
# (Same import handling as x1.py)
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:")
    print(f"1. TIA Portal Openness for V{TIA_PORTAL_VERSION} is installed.")
    print(
        "2. The 'siemens_tia_scripting' Python module is installed (pip install ...) or"
    )
    print(
        "   the path to its binaries is set in the 'TIA_SCRIPTING' environment variable."
    )
    print(
        "3. You are using a compatible Python version (e.g., 3.12.X as per documentation)."
    )
    sys.exit(1)
except Exception as e:
    print(f"An unexpected error occurred during import: {e}")
    traceback.print_exc()
    sys.exit(1)

# --- Functions ---


def select_project_file():
    """Opens a dialog to select a TIA Portal project file."""
    root = tk.Tk()
    root.withdraw()  # Hide the main tkinter window
    file_path = filedialog.askopenfilename(
        title="Select TIA Portal Project File",
        filetypes=[
            (
                f"TIA Portal V{TIA_PORTAL_VERSION} Projects",
                f"*.ap{TIA_PORTAL_VERSION.split('.')[0]}",
            )
        ],  # e.g. *.ap18
    )
    root.destroy()
    if not file_path:
        print("No project file selected. Exiting.")
        sys.exit(0)
    return file_path


def export_plc_cross_references(plc, export_base_dir):
    """Exports cross-references for various elements from a given PLC."""
    plc_name = plc.get_name()
    print(f"\n--- Processing PLC: {plc_name} ---")

    # Define base export path for this PLC's cross-references
    plc_export_dir = export_base_dir / plc_name
    plc_export_dir.mkdir(parents=True, exist_ok=True)  # Use pathlib's mkdir

    # --- Export Program Block Cross-References ---
    blocks_cr_exported = 0
    blocks_cr_skipped = 0
    print(f"\n[PLC: {plc_name}] Exporting Program Block Cross-References...")
    blocks_cr_path = plc_export_dir / "ProgramBlocks_CR"
    blocks_cr_path.mkdir(exist_ok=True)
    print(f"  Target: {blocks_cr_path}")

    try:
        # Assuming get_program_blocks() doesn't need folder_path to get all blocks
        program_blocks = plc.get_program_blocks()
        print(f"  Found {len(program_blocks)} program blocks.")
        for block in program_blocks:
            block_name = block.get_name()
            print(f"    Processing block: {block_name}...")
            try:
                # Note: Consistency check might not be needed/available before cross-ref export
                print(f"      Exporting cross-references for {block_name}...")
                block.export_cross_references(
                    target_directorypath=str(
                        blocks_cr_path
                    ),  # API likely needs string path
                    filter=CROSS_REF_FILTER,
                )
                blocks_cr_exported += 1
            except RuntimeError as block_ex:
                print(
                    f"      TIA ERROR exporting cross-references for block {block_name}: {block_ex}"
                )
                blocks_cr_skipped += 1
            except Exception as block_ex:
                print(
                    f"      GENERAL ERROR exporting cross-references for block {block_name}: {block_ex}"
                )
                traceback.print_exc()  # Print stack trace for general errors
                blocks_cr_skipped += 1
        print(
            f"  Program Block CR Export Summary: Exported={blocks_cr_exported}, Skipped/Errors={blocks_cr_skipped}"
        )
    except AttributeError:
        print(
            "  AttributeError: Could not find 'get_program_blocks' on PLC object. Skipping Program Blocks."
        )
    except Exception as e:
        print(f"  ERROR accessing Program Blocks for cross-reference export: {e}")
        traceback.print_exc()

    # --- Export PLC Tag Table Cross-References ---
    tags_cr_exported = 0
    tags_cr_skipped = 0
    print(f"\n[PLC: {plc_name}] Exporting PLC Tag Table Cross-References...")
    tags_cr_path = plc_export_dir / "PlcTags_CR"
    tags_cr_path.mkdir(exist_ok=True)
    print(f"  Target: {tags_cr_path}")

    try:
        # Assuming get_plc_tag_tables() doesn't need folder_path to get all tables
        tag_tables = plc.get_plc_tag_tables()
        print(f"  Found {len(tag_tables)} Tag Tables.")
        for table in tag_tables:
            table_name = table.get_name()
            print(f"    Processing Tag Table: {table_name}...")
            try:
                print(f"      Exporting cross-references for {table_name}...")
                table.export_cross_references(
                    target_directorypath=str(tags_cr_path), filter=CROSS_REF_FILTER
                )
                tags_cr_exported += 1
            except RuntimeError as table_ex:
                print(
                    f"      TIA ERROR exporting cross-references for Tag Table {table_name}: {table_ex}"
                )
                tags_cr_skipped += 1
            except Exception as table_ex:
                print(
                    f"      GENERAL ERROR exporting cross-references for Tag Table {table_name}: {table_ex}"
                )
                traceback.print_exc()
                tags_cr_skipped += 1
        print(
            f"  Tag Table CR Export Summary: Exported={tags_cr_exported}, Skipped/Errors={tags_cr_skipped}"
        )
    except AttributeError:
        print(
            "  AttributeError: Could not find 'get_plc_tag_tables' on PLC object. Skipping Tag Tables."
        )
    except Exception as e:
        print(f"  ERROR accessing Tag Tables for cross-reference export: {e}")
        traceback.print_exc()

    # --- Export PLC Data Type (UDT) Cross-References ---
    udts_cr_exported = 0
    udts_cr_skipped = 0
    print(f"\n[PLC: {plc_name}] Exporting PLC Data Type (UDT) Cross-References...")
    udts_cr_path = plc_export_dir / "PlcDataTypes_CR"
    udts_cr_path.mkdir(exist_ok=True)
    print(f"  Target: {udts_cr_path}")

    try:
        # Assuming get_user_data_types() doesn't need folder_path to get all UDTs
        udts = plc.get_user_data_types()
        print(f"  Found {len(udts)} UDTs.")
        for udt in udts:
            udt_name = udt.get_name()
            print(f"    Processing UDT: {udt_name}...")
            try:
                print(f"      Exporting cross-references for {udt_name}...")
                udt.export_cross_references(
                    target_directorypath=str(udts_cr_path), filter=CROSS_REF_FILTER
                )
                udts_cr_exported += 1
            except RuntimeError as udt_ex:
                print(
                    f"      TIA ERROR exporting cross-references for UDT {udt_name}: {udt_ex}"
                )
                udts_cr_skipped += 1
            except Exception as udt_ex:
                print(
                    f"      GENERAL ERROR exporting cross-references for UDT {udt_name}: {udt_ex}"
                )
                traceback.print_exc()
                udts_cr_skipped += 1
        print(
            f"  UDT CR Export Summary: Exported={udts_cr_exported}, Skipped/Errors={udts_cr_skipped}"
        )
    except AttributeError:
        print(
            "  AttributeError: Could not find 'get_user_data_types' on PLC object. Skipping UDTs."
        )
    except Exception as e:
        print(f"  ERROR accessing UDTs for cross-reference export: {e}")
        traceback.print_exc()

    # --- Export System Block Cross-References ---
    sys_blocks_cr_exported = 0
    sys_blocks_cr_skipped = 0
    print(f"\n[PLC: {plc_name}] Attempting to Export System Block Cross-References...")
    sys_blocks_cr_path = plc_export_dir / "SystemBlocks_CR"
    sys_blocks_cr_path.mkdir(exist_ok=True)
    print(f"  Target: {sys_blocks_cr_path}")

    try:
        # Check if method exists before calling
        if hasattr(plc, "get_system_blocks"):
            system_blocks = plc.get_system_blocks()
            print(
                f"  Found {len(system_blocks)} system blocks (using get_system_blocks)."
            )
            for sys_block in system_blocks:
                sys_block_name = sys_block.get_name()
                print(f"    Processing System Block: {sys_block_name}...")
                try:
                    print(f"      Exporting cross-references for {sys_block_name}...")
                    sys_block.export_cross_references(
                        target_directorypath=str(sys_blocks_cr_path),
                        filter=CROSS_REF_FILTER,
                    )
                    sys_blocks_cr_exported += 1
                except RuntimeError as sys_ex:
                    print(
                        f"      TIA ERROR exporting cross-references for System Block {sys_block_name}: {sys_ex}"
                    )
                    sys_blocks_cr_skipped += 1
                except Exception as sys_ex:
                    print(
                        f"      GENERAL ERROR exporting cross-references for System Block {sys_block_name}: {sys_ex}"
                    )
                    traceback.print_exc()
                    sys_blocks_cr_skipped += 1
        else:
            print(
                "  Method 'get_system_blocks' not found on PLC object. Skipping System Blocks."
            )
            # Alternative: Try navigating DeviceItems if needed, but that's more complex.

        print(
            f"  System Block CR Export Summary: Exported={sys_blocks_cr_exported}, Skipped/Errors={sys_blocks_cr_skipped}"
        )
    except AttributeError:  # Catch if get_name() or other methods fail on sys_block
        print(
            "  AttributeError during System Block processing. Skipping remaining System Blocks."
        )
        traceback.print_exc()
    except Exception as e:
        print(
            f"  ERROR accessing/processing System Blocks for cross-reference export: {e}"
        )
        traceback.print_exc()

    # --- Export Software Unit Cross-References ---
    sw_units_cr_exported = 0
    sw_units_cr_skipped = 0
    print(f"\n[PLC: {plc_name}] Attempting to Export Software Unit Cross-References...")
    sw_units_cr_path = plc_export_dir / "SoftwareUnits_CR"
    sw_units_cr_path.mkdir(exist_ok=True)
    print(f"  Target: {sw_units_cr_path}")

    try:
        # Check if method exists before calling
        if hasattr(plc, "get_software_units"):
            software_units = plc.get_software_units()
            print(f"  Found {len(software_units)} Software Units.")
            for unit in software_units:
                unit_name = unit.get_name()
                print(f"    Processing Software Unit: {unit_name}...")
                try:
                    print(f"      Exporting cross-references for {unit_name}...")
                    unit.export_cross_references(
                        target_directorypath=str(sw_units_cr_path),
                        filter=CROSS_REF_FILTER,
                    )
                    sw_units_cr_exported += 1
                except RuntimeError as unit_ex:
                    print(
                        f"      TIA ERROR exporting cross-references for Software Unit {unit_name}: {unit_ex}"
                    )
                    sw_units_cr_skipped += 1
                except Exception as unit_ex:
                    print(
                        f"      GENERAL ERROR exporting cross-references for Software Unit {unit_name}: {unit_ex}"
                    )
                    traceback.print_exc()
                    sw_units_cr_skipped += 1
            print(
                f"  Software Unit CR Export Summary: Exported={sw_units_cr_exported}, Skipped/Errors={sw_units_cr_skipped}"
            )
        else:
            print(
                "  Method 'get_software_units' not found on PLC object. Skipping Software Units."
            )
    except AttributeError:  # Catch if get_name() or other methods fail on unit
        print(
            "  AttributeError during Software Unit processing. Skipping remaining Software Units."
        )
        traceback.print_exc()
    except Exception as e:
        print(
            f"  ERROR accessing/processing Software Units for cross-reference export: {e}"
        )
        traceback.print_exc()

    print(f"\n--- Finished processing PLC: {plc_name} ---")


# --- Main Script ---

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

    print("--- TIA Portal Cross-Reference Exporter ---")

    # 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
    project_file = select_project_file()

    # 2. Define Export Directory using working_directory and subfolder
    # The export base directory is the working directory. PLC-specific folders will be created inside.
    export_base_dir = Path(working_directory)
    try:
        # Ensure the base working directory exists (it should, but check doesn't hurt)
        export_base_dir.mkdir(parents=True, exist_ok=True)
        print(f"\nSelected Project: {project_file}")
        print(f"Using Base Export Directory: {export_base_dir.resolve()}")
    except Exception as e:
        print(f"ERROR: Could not create export directory '{export_dir}'. Error: {e}")
        sys.exit(1)

    portal_instance = None
    project_object = None

    try:
        # 3. Connect to TIA Portal
        print(f"\nConnecting to TIA Portal V{TIA_PORTAL_VERSION}...")
        # Connect using WithGraphicalUserInterface mode for visibility
        portal_instance = ts.open_portal(
            version=TIA_PORTAL_VERSION,
            portal_mode=ts.Enums.PortalMode.WithGraphicalUserInterface,
        )
        print("Connected to TIA Portal.")
        print(f"Portal Process ID: {portal_instance.get_process_id()}")

        # 4. Open Project
        print(f"Opening project: {os.path.basename(project_file)}...")
        project_path_obj = Path(project_file)  # Use Path object
        project_object = portal_instance.open_project(
            project_file_path=str(project_path_obj)
        )
        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 successfully.")

        # 5. Get PLCs
        plcs = project_object.get_plcs()
        if not plcs:
            print("No PLC devices found in the project.")
        else:
            print(
                f"Found {len(plcs)} PLC(s). Starting cross-reference export process..."
            )

            # 6. Iterate and Export Cross-References for each PLC
            for plc_device in plcs:
                export_plc_cross_references(
                    plc=plc_device,
                    export_base_dir=export_base_dir, # Pass the base directory
                )

        print("\nCross-reference export process completed.")

    except RuntimeError 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: {e}")
        traceback.print_exc()
    finally:
        # 7. Cleanup
        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}")

        print("\nScript finished.")
