"""Resource Hacker - Main application entry point.

This is the main entry point for the Resource Hacker application,
providing both GUI and command-line interfaces.
"""

from __future__ import annotations

import argparse
import logging
import sys
from pathlib import Path

from .models.file_utils import FileUtils
from .models.resource_manager import ResourceManager

logging.basicConfig(
    level=logging.INFO,
    format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
)
logger = logging.getLogger(__name__)


def list_resources(file_path: str) -> None:
    """List resources in a PE file."""
    try:
        file_path_obj = Path(file_path)

        # Validate file
        if not file_path_obj.exists():
            logger.error(f"Error: File not found: {file_path}")
            sys.exit(1)

        if not FileUtils.is_pe_file(file_path_obj):
            logger.error(f"Error: Not a valid PE file: {file_path}")
            sys.exit(1)

        # Load and analyze file
        with ResourceManager() as rm:
            if not rm.load_pe_file(file_path_obj):
                logger.error(f"Error: Failed to load PE file: {file_path}")
                sys.exit(1)

            # Get file info
            file_info = rm.get_file_info()
            if file_info:
                logger.info(f"File: {file_info['file_name']}")
                logger.info(f"Machine: {file_info['machine']}")
                logger.info(f"Subsystem: {file_info['subsystem']}")
                logger.info(f"Type: {'DLL' if file_info['is_dll'] else 'Executable'}")
                logger.info(
                    f"Architecture: {'64-bit' if file_info['is_64bit'] else '32-bit'}",
                )
                logger.info(f"Resources: {file_info['resource_count']}")
                logger.info()

            # Get resource types
            resource_types = rm.get_resource_types()
            if resource_types:
                logger.info("Resource Types:")
                for type_id, type_name in sorted(resource_types.items()):
                    resources = rm.get_resources_by_type(type_id)
                    logger.info(
                        f"  {type_id:2d} {type_name:<20} ({len(resources)} resources)",
                    )
                logger.info()

            # List all resources
            resources = rm.get_all_resources()
            if resources:
                logger.info("All Resources:")
                logger.info(
                    f"{'Type':<25} {'Name':<10} {'Size':<10} {'Language':<8} {'Offset':<10}",
                )
                logger.info("-" * 70)
                for resource in sorted(resources, key=lambda r: (r.type_id, r.name_id)):
                    size_str = FileUtils.format_file_size(resource.size)
                    logger.info(
                        f"{resource.type_name:<25} {resource.name_id:<10} {size_str:<10} "
                        f"{resource.language_id:<8} 0x{resource.offset:08x}",
                    )
            else:
                logger.info("No resources found in the file.")

    except Exception as e:
        logger.exception(f"Error listing resources: {e}")
        logger.exception(f"Error: {e}")
        sys.exit(1)


def extract_resource(
    file_path: str,
    type_id: int,
    name_id: int,
    language_id: int,
    output_path: str | None = None,
) -> None:
    """Extract a specific resource from a PE file."""
    try:
        file_path_obj = Path(file_path)

        # Validate file
        if not file_path_obj.exists():
            logger.error(f"Error: File not found: {file_path}")
            sys.exit(1)

        if not FileUtils.is_pe_file(file_path_obj):
            logger.error(f"Error: Not a valid PE file: {file_path}")
            sys.exit(1)

        # Determine output path
        if output_path is None:
            output_path = f"{file_path_obj.stem}_resource_{type_id}_{name_id}_{language_id}.bin"

        output_path_obj = Path(output_path)

        # Validate output path
        if not FileUtils.validate_output_path(output_path_obj):
            logger.error(f"Error: Cannot write to output path: {output_path}")
            sys.exit(1)

        # Extract resource
        with ResourceManager() as rm:
            if not rm.load_pe_file(file_path_obj):
                logger.error(f"Error: Failed to load PE file: {file_path}")
                sys.exit(1)

            if rm.export_resource(type_id, name_id, output_path_obj, language_id):
                logger.info(f"Successfully extracted resource to: {output_path}")
                logger.info(
                    f"Size: {FileUtils.format_file_size(output_path_obj.stat().st_size)}",
                )
            else:
                logger.error("Error: Resource not found or extraction failed")
                sys.exit(1)

    except Exception as e:
        logger.exception(f"Error extracting resource: {e}")
        logger.exception(f"Error: {e}")
        sys.exit(1)


def replace_resource(
    file_path: str,
    type_id: int,
    name_id: int,
    input_path: str,
    language_id: int = 0,
) -> None:
    """Replace a resource in a PE file with new data."""
    try:
        file_path_obj = Path(file_path)
        input_path_obj = Path(input_path)

        # Validate files
        if not file_path_obj.exists():
            logger.error(f"Error: PE file not found: {file_path}")
            sys.exit(1)

        if not input_path_obj.exists():
            logger.error(f"Error: Input file not found: {input_path}")
            sys.exit(1)

        if not FileUtils.is_pe_file(file_path_obj):
            logger.error(f"Error: Not a valid PE file: {file_path}")
            sys.exit(1)

        # Read new resource data
        new_data = input_path_obj.read_bytes()
        logger.info(f"Loading new resource data: {len(new_data)} bytes from {input_path}")

        # Replace resource
        with ResourceManager() as rm:
            if not rm.load_pe_file(file_path_obj):
                logger.error(f"Error: Failed to load PE file: {file_path}")
                sys.exit(1)

            # Check if resource exists
            resource_info = rm.get_resource_info(type_id, name_id, language_id)
            if not resource_info:
                logger.error(f"Error: Resource not found: Type={type_id}, Name={name_id}")
                logger.info("Available resources:")
                for resource in rm.get_all_resources():
                    if resource.type_id == type_id:
                        logger.info(
                            f"  Name: {resource.name_id}, Language: {resource.language_id}",
                        )
                sys.exit(1)

            logger.info(
                f"Found resource: {resource_info['type_name']} - "
                f"{resource_info['name']} ({resource_info['size']} bytes)",
            )

            # Perform the replacement
            if rm.replace_resource(type_id, name_id, new_data, language_id):
                logger.info(f"✓ Successfully replaced resource in: {file_path}")
                logger.info(f"  Original size: {resource_info['size']} bytes")
                logger.info(f"  New size: {len(new_data)} bytes")
            else:
                logger.error("✗ Failed to replace resource")
                sys.exit(1)

    except Exception as e:
        logger.exception(f"Error replacing resource: {e}")
        logger.exception(f"Error: {e}")
        sys.exit(1)


def main() -> None:
    """Run main application entry point."""
    parser = argparse.ArgumentParser(
        description="Resource Hacker - Extract and modify Windows executable resources",
        formatter_class=argparse.RawDescriptionHelpFormatter,
        epilog="""
Examples:
  reshack list file.exe                # List resources in PE file
  reshack extract file.exe 3 100       # Extract icon resource ID 100
  reshack replace file.exe 3 100 new.ico  # Replace icon resource with new.ico
        """,
    )

    # Add subcommands
    subparsers = parser.add_subparsers(dest="command", help="Available commands")

    # List command
    list_parser = subparsers.add_parser("list", help="List resources in PE file")
    list_parser.add_argument("file", help="PE file to analyze")

    # Extract command
    extract_parser = subparsers.add_parser(
        "extract",
        help="Extract resource from PE file",
    )
    extract_parser.add_argument("file", help="PE file to extract from")
    extract_parser.add_argument("type_id", type=int, help="Resource type ID")
    extract_parser.add_argument("name_id", type=int, help="Resource name ID")
    extract_parser.add_argument(
        "--language",
        type=int,
        default=0,
        help="Language ID (default: 0)",
    )
    extract_parser.add_argument("--output", "-o", help="Output file path")

    # Replace command
    replace_parser = subparsers.add_parser(
        "replace",
        help="Replace resource in PE file",
    )
    replace_parser.add_argument("file", help="PE file to modify")
    replace_parser.add_argument("type_id", type=int, help="Resource type ID")
    replace_parser.add_argument("name_id", type=int, help="Resource name ID")
    replace_parser.add_argument("input", help="Input file with new resource data")
    replace_parser.add_argument(
        "--language",
        type=int,
        default=0,
        help="Language ID (default: 0)",
    )

    # Parse arguments
    args = parser.parse_args()

    # Handle commands
    if args.command == "list":
        list_resources(args.file)
    elif args.command == "extract":
        extract_resource(
            args.file,
            args.type_id,
            args.name_id,
            args.language,
            args.output,
        )
    elif args.command == "replace":
        replace_resource(
            args.file,
            args.type_id,
            args.name_id,
            args.input,
            args.language,
        )
    else:
        parser.print_help()


if __name__ == "__main__":
    main()
