"""
UDA Acquisition Pipeline - YAML Configuration and CLI Tool

"""

from datetime import datetime
import yaml
import re
import typer
import click
from cruxctl.command_groups.data_acquisition.meta_store import MetaStoreClient

from cruxctl.command_groups.data_acquisition.models import (
    AcquisitionConfig,
    ValidationConfig,
    DetectPattern,
    PatternType,
    SpecConfig,
    SinkConfig,
    SinkType,
    DeployConfig,
    DeployMode,
    MetadataConfig,
    SourceConfig,
    SourceType,
)

app = typer.Typer(help="UDA Acquisition Pipeline Management")


def validate_not_empty(value: str, field_name: str) -> str:
    """Validate that a field is not empty"""
    if not value or value.strip() == "":
        raise ValueError(f"{field_name} cannot be empty")
    return value.strip()


def validate_cron_expression(cron_expr: str, field_name: str = None) -> bool:
    """Validate 5-part cron expression"""
    parts = cron_expr.strip().split()
    if len(parts) != 5:
        return False

    cron_pattern = r"^[\d\*\-\,\/]+$"
    for part in parts:
        if not re.match(cron_pattern, part):
            return False

    return True


def validate_date_format(date_str: str, field_name: str) -> bool:
    """Validate date format (YYYY-MM-DD)"""
    if not date_str:
        return True  # Optional field
    try:
        datetime.strptime(date_str, "%Y-%m-%d")
        return True
    except ValueError:
        typer.echo(f"  Invalid date format for {field_name}. Expected YYYY-MM-DD")
        return False


def validate_regex_pattern(pattern: str, field_name: str) -> bool:
    """Validate regex pattern syntax"""
    if not pattern:
        return True  # Optional field
    try:
        re.compile(pattern)
        return True
    except re.error as e:
        typer.echo(f"  Invalid regex pattern for {field_name}: {str(e)}")
        return False


def prompt_with_validation(
    prompt_text: str,
    validator=None,
    default=None,
    allow_empty=False,
    field_name=None,
    error_message=None,
) -> str:
    """Prompt user with validation"""
    while True:
        if default is not None:
            value = typer.prompt(prompt_text, default=default)
        else:
            value = typer.prompt(prompt_text, default="", show_default=False)
        if not value or value.strip() == "":
            if allow_empty:
                return ""
            else:
                typer.echo(
                    f" {field_name or 'This field'} is required and cannot be empty"
                )
                continue
        if validator:
            if validator(value.strip(), field_name or prompt_text):
                return value.strip()
            else:
                if error_message:
                    typer.echo(f" {error_message}")
                continue
        return value.strip()


# CLI Commands


@app.command()
def validate(config_file: str):
    """Validate a YAML configuration file"""
    try:
        with open(config_file, "r") as f:
            config_dict = yaml.safe_load(f)

        config = AcquisitionConfig.from_dict(config_dict)

        typer.echo("✓ Configuration is valid!")
        typer.echo(f"\n  Downloader: {config.metadata.uda_id}")
        typer.echo(f"  Shard: {config.metadata.uda_shard_id}")
        typer.echo(f"  Org: {config.metadata.org_id}")
        typer.echo(f"  Source: {config.spec.source.type.value}")
        typer.echo(f"  Deploy Mode: {config.spec.deploy.mode.value}")
        typer.echo(f"  Deploy Frequency: {config.spec.deploy.frequency}")
        typer.echo(f"  Patterns: {len(config.spec.detect)} defined")

    except Exception as e:
        typer.echo(f"Validation failed: {str(e)}", err=True)
        raise typer.Exit(1)


@app.command()
def list_downloaders():
    """List all downloaders in the database"""
    try:
        client = MetaStoreClient()
        downloaders = client.get_all_downloaders()

        if not downloaders:
            typer.echo("No downloaders found in database")
            return

        typer.echo(f"\nFound {len(downloaders)} downloader(s):\n")
        typer.echo(
            f"{'ID':<35} {'Key':<30} {'Shard':<8} {'Type':<8} {'Mode':<12} {'Status':<8}"
        )
        typer.echo("-" * 110)
        for dl in downloaders:
            status = "Active" if dl.get("status") else "Inactive"
            typer.echo(
                f"{dl['id']:<35} {dl['key']:<30} {dl['shard_num']:<8} "
                f"{dl['source_type']:<8} {dl.get('deploy_mode', 'N/A'):<12} {status:<8}"
            )
    except Exception as e:
        typer.echo(f"Error listing downloaders: {str(e)}", err=True)
        raise typer.Exit(1)


@app.command()
def show_downloader(downloader_id: str):
    """Show details of a specific downloader"""
    try:

        # Get downloader info
        client = MetaStoreClient()
        response = client.get_downloader_by_id(downloader_id)
        downloader = response["downloader"]
        if not downloader:
            typer.echo(f"Downloader not found: {downloader_id}")
            raise typer.Exit(1)

        typer.echo("\nDownloader Details:")
        typer.echo(f"  ID: {downloader['id']}")
        typer.echo(f"  Key: {downloader['key']}")
        typer.echo(f"  Shard: {downloader['shard_num']}")
        typer.echo(f"  Org ID: {downloader.get('supplier_org_id', 'N/A')}")
        typer.echo(f"  Source Type: {downloader['source_type']}")
        typer.echo(f"  Secret Key: {downloader.get('secret_key', 'N/A')}")
        typer.echo(f"  Deploy Mode: {downloader.get('deploy_mode', 'N/A')}")
        typer.echo(f"  Frequency: {downloader.get('frequency', 'N/A')}")
        typer.echo(f"  Sink Bucket: {downloader.get('sink_bucket', 'N/A')}")
        typer.echo(f"  Sink Prefix: {downloader.get('sink_prefix', 'N/A')}")
        typer.echo(f"  Status: {'Active' if downloader.get('status') else 'Inactive'}")
        typer.echo(f"  Created: {downloader.get('created_at')}")

        # Get patterns
        patterns = response["patterns"]

        if patterns:
            typer.echo(f"\n  Patterns ({len(patterns)}):")
            # Iterate through all patterns
            for pattern_obj in patterns:
                pattern = pattern_obj["pattern"]
                rules = pattern_obj["validation_rules"]

                # Use pattern fields
                print(
                    f"Pattern: {pattern['id']}: "
                    f"{pattern['folder_path']}/{pattern['file_pattern']}"
                )

                # Use rules
                for rule in rules:
                    config = rule["validation_config"]
                    print(
                        f"Validation Rules:  Z-scores: "
                        f"size={config['size_z_score']}, count={config['count_z_score']}"
                    )

    # Get validation rules
    except Exception as e:
        typer.echo(f"Error showing downloader: {str(e)}", err=True)
        raise typer.Exit(1)


@app.command()
def create(
    output_path: str = typer.Option("config.yaml", help="Output YAML file path"),
    interactive: bool = typer.Option(True, help="Interactive mode"),
    store_in_db: bool = typer.Option(
        False, help="Store configuration in database after creation"
    ),
):
    """Create a new acquisition configuration"""
    if interactive:
        typer.echo("\n=== UDA Acquisition Configuration Generator ===\n")

        # Metadata
        typer.echo("1. Metadata Configuration")
        uda_id = prompt_with_validation(
            "UDA ID (downloader name)",
            validator=validate_not_empty,
            field_name="UDA ID",
        )
        uda_shard_id = int(typer.prompt("UDA Shard ID", default=1))
        org_id = prompt_with_validation(
            "Organization ID",
            validator=validate_not_empty,
            field_name="Organization ID",
        )

        # Deploy configuration
        typer.echo("\n2. Deployment Configuration")
        deploy_mode = typer.prompt(
            "Deploy Mode",
            type=click.Choice(["CONTINUOUS", "SCHEDULED"], case_sensitive=False),
            default="SCHEDULED",
        ).upper()

        if deploy_mode == "CONTINUOUS":
            deploy_frequency = "@Once"
            typer.echo(f"  ✓ Frequency set to: {deploy_frequency} (continuous mode)")
        else:  # SCHEDULED
            typer.echo(
                "  Enter cron expression (5 parts: minute hour day month weekday)"
            )
            typer.echo("  Example: '*/30 * * * *' for every 30 minutes")
            deploy_frequency = prompt_with_validation(
                "Cron Expression (5 parts: min hour day month weekday)",
                validator=validate_cron_expression,
                field_name="Cron Expression",
                default="*/30 * * * *",
            )

        typer.echo("\n3. Source Configuration")
        source_type = typer.prompt(
            "Source Type",
            type=click.Choice(["FTP", "SFTP", "S3", "GCS"], case_sensitive=False),
            default="FTP",
        ).upper()

        source_key = prompt_with_validation(
            "Secret Key", validator=validate_not_empty, field_name="Secret Key"
        )

        # Detection patterns
        typer.echo("\n5. Detection Patterns")
        detect_patterns = []

        # INCLUDE patterns
        pattern_count = 0
        exclude_count = 0
        while True:
            pattern_type = typer.prompt(
                f"Pattern #{len(detect_patterns) + 1} Type (INCLUDE/EXCLUDE, or 'done' to finish)",
                default="INCLUDE" if pattern_count == 0 else "done",
            ).upper()

            # Validations for this pattern
            if pattern_type.lower() == "done":
                if pattern_count == 0:
                    typer.echo("  At least one INCLUDE pattern is required")
                    continue
                break

            # EXCLUDE patterns
            if pattern_type not in ["INCLUDE", "EXCLUDE"]:
                typer.echo("  Invalid pattern type. Use INCLUDE or EXCLUDE")
                continue
            folder_pattern = typer.prompt("Folder Pattern", default="/")
            file_pattern = prompt_with_validation(
                "File Pattern", validator=validate_not_empty, field_name="File Pattern"
            )
            vendor_date_regex = None
            if pattern_type == "INCLUDE":
                vendor_date_regex = typer.prompt(
                    "Vendor Date Regex (optional, press Enter to skip)",
                    default="",
                    show_default=False,
                )
                if vendor_date_regex and not validate_regex_pattern(
                    vendor_date_regex, "Vendor Date Regex"
                ):
                    raise typer.Exit(1)
                vendor_date_regex = vendor_date_regex if vendor_date_regex else None

                validations = []
                if typer.confirm(
                    "Add validation rules for this pattern?", default=False
                ):
                    while True:
                        typer.echo(f"\nValidation Rule #{len(validations) + 1}")
                        val_folder = typer.prompt(
                            "Validation Folder", default=folder_pattern
                        )
                        val_pattern = typer.prompt(
                            "Validation File Pattern", default=file_pattern
                        )
                        add_count = typer.confirm(
                            "Add file count validation?", default=False
                        )
                        file_count = None
                        if add_count:
                            z_score = float(
                                typer.prompt("Z-score threshold", default=0.05)
                            )
                            file_count = {"z_score": z_score}
                        add_size = typer.confirm(
                            "Add file size validation?", default=False
                        )
                        size = None
                        if add_size:
                            z_score = float(
                                typer.prompt("Z-score threshold", default=0.05)
                            )
                            size = {"z_score": z_score}
                        validations.append(
                            ValidationConfig(
                                folder=val_folder,
                                pattern=val_pattern,
                                file_count=file_count,
                                size=size,
                            )
                        )
                        if not typer.confirm(
                            "    Add another validation rule?", default=False
                        ):
                            break
            detect_patterns.append(
                DetectPattern(
                    type=PatternType(pattern_type),
                    folder_pattern=folder_pattern,
                    file_pattern=file_pattern,
                    vendor_date_regex=vendor_date_regex,
                    validations=(
                        validations
                        if pattern_type == "INCLUDE" and validations
                        else None
                    ),
                )
            )
            if pattern_type == "INCLUDE":
                pattern_count += 1
            else:
                exclude_count += 1

        # Sink
        typer.echo("\n6. Sink Configuration")
        sink_type = typer.prompt("Sink Type", default="GCS")
        bucket_name = prompt_with_validation(
            "Bucket Name", validator=validate_not_empty, field_name="Bucket Name"
        )
        prefix_path = typer.prompt(
            "Prefix Path (optional)", default="", show_default=False
        )

        retention_policy = None
        if typer.confirm("\nAdd retention policy?", default=False):
            retention_policy = {"days": int(typer.prompt("Retention days", default=30))}

        # Build configuration
        config = AcquisitionConfig(
            version="0.0.1",
            kind="acquisition",
            metadata=MetadataConfig(
                uda_id=uda_id, uda_shard_id=uda_shard_id, org_id=org_id
            ),
            spec=SpecConfig(
                deploy=DeployConfig(
                    mode=DeployMode(deploy_mode), frequency=deploy_frequency
                ),
                source=SourceConfig(
                    type=SourceType(source_type.upper()), key=source_key
                ),
                detect=detect_patterns,
                sink=SinkConfig(
                    type=SinkType(sink_type),
                    bucket_name=bucket_name,
                    prefix_path=prefix_path if prefix_path else None,
                    retention_policy=retention_policy if retention_policy else None,
                ),
            ),
        )

        # Convert to dict and save YAML
        config_dict = config.to_dict()
        with open(output_path, "w") as f:
            yaml.dump(config_dict, f, default_flow_style=False, sort_keys=False)

        typer.echo(f"\n✓ Configuration saved to: {output_path}")
        typer.echo("\nSummary:")
        typer.echo(f"  - UDA ID: {uda_id}")
        typer.echo(f"  - Shard ID: {uda_shard_id}")
        typer.echo(f"  - Organization: {org_id}")
        typer.echo(f"  - Deploy Mode: {deploy_mode}")
        typer.echo(f"  - Frequency: {deploy_frequency}")
        typer.echo(f"  - Source: {source_type}")
        typer.echo(f"  - INCLUDE Patterns: {pattern_count}")
        typer.echo(f"  - EXCLUDE Patterns: {exclude_count}")
        typer.echo(f"  - Sink: gs://{bucket_name}/{prefix_path}")

        # Non-interactive mode - create template
        template_config = {
            "version": "0.0.1",
            "kind": "acquisition",
            "metadata": {
                "uda_id": "my-scanner",
                "uda_shard_id": 1,
                "org_id": "Or12345",
            },
            "spec": {
                "deploy": {"mode": "SCHEDULED", "frequency": "*/30 * * * *"},
                "source": {"type": "FTP", "key": "<CONNECTIONS_KEY>"},
                "detect": [
                    {
                        "type": "INCLUDE",
                        "folder_pattern": "/folder1",
                        "file_pattern": "*.csv",
                        "vendor_date_regex": None,
                        "validations": None,
                    },
                    {
                        "type": "EXCLUDE",
                        "folder_pattern": "/folder1",
                        "file_pattern": "*.pdf",
                        "vendor_date_regex": None,
                        "validations": None,
                    },
                ],
                "sink": {
                    "type": "GCS",
                    "bucket_name": "uda-bronze-lake",
                    "prefix_path": "my-prefix",
                    "retention_policy": None,
                },
            },
        }
        with open(output_path, "w") as f:
            yaml.dump(template_config, f, default_flow_style=False, sort_keys=False)
        typer.echo(f"✓ Template configuration saved to: {output_path}")
        typer.echo(
            f"Edit the file and run: python data_acquisition.py validate {output_path}"
        )


@app.command()
def export(
    downloader_id: str = typer.Argument(..., help="Downloader ID to export"),
    output_path: str = typer.Option(
        "exported_config.yaml", help="Output YAML file path"
    ),
):
    """Export a downloader configuration from database to YAML file"""
    try:
        client = MetaStoreClient()
        downloader = client.get_downloader_by_id(downloader_id)
        if not downloader:
            typer.echo(f"Downloader not found: {downloader_id}")
            raise typer.Exit(1)
        typer.echo(f"Exporting downloader: {downloader['key']}")
        patterns = client.get_patterns_by_downloader(downloader_id)
        if not patterns:
            typer.echo(f"No patterns found for downloader: {downloader_id}")
            raise typer.Exit(1)

        detect_patterns = []
        for pattern in patterns:
            # Get validation rules for this pattern
            validation_rules = client.get_validation_rules_by_pattern(pattern["id"])

            # Build validations list
            validations = None
            if validation_rules and pattern["type"] == "INCLUDE":
                validations = []
                for rule in validation_rules:
                    val_config = rule["validation_config"]
                    validation = {
                        "folder": pattern["folder_path"],
                        "pattern": pattern["file_pattern"],
                    }
                    if "count_z_score" in val_config:
                        validation["file_count"] = {
                            "z_score": val_config["count_z_score"]
                        }
                    if "size_z_score" in val_config:
                        validation["size"] = {"z_score": val_config["size_z_score"]}
                    validations.append(validation)
            detect_pattern = {
                "type": pattern["type"],
                "folder_pattern": pattern["folder_path"],
                "file_pattern": pattern["file_pattern"],
                "vendor_date_regex": pattern.get("vendor_date_regex"),
                "validations": validations,
            }
            detect_patterns.append(detect_pattern)
        config_dict = {
            "version": "0.0.1",
            "kind": "acquisition",
            "metadata": {
                "uda_id": downloader["key"],
                "uda_shard_id": downloader["shard_num"],
                "org_id": downloader.get("supplier_org_id", ""),
            },
            "spec": {
                "deploy": {
                    "mode": downloader.get("deploy_mode", "SCHEDULED"),
                    "frequency": downloader.get("frequency", "*/30 * * * *"),
                },
                "source": {
                    "type": downloader["source_type"],
                    "key": downloader.get("secret_key", "<SECRET_KEY>"),
                },
                "detect": detect_patterns,
                "sink": {
                    "type": "gcs",
                    "bucket_name": downloader.get("sink_bucket", ""),
                    "prefix_path": downloader.get("sink_prefix"),
                    "retention_policy": downloader.get("retention_policy"),
                },
            },
        }
        with open(output_path, "w") as f:
            yaml.dump(config_dict, f, default_flow_style=False, sort_keys=False)
        typer.echo(f"\n✓ Configuration exported to: {output_path}")
        typer.echo("\nSummary:")
        typer.echo(f"  - UDA ID: {downloader['key']}")
        typer.echo(f"  - Shard ID: {downloader['shard_num']}")
        typer.echo(f"  - Patterns: {len(detect_patterns)} exported")
        typer.echo(
            f"  - Validation Rules: {sum(1 for p in detect_patterns if p.get('validations'))}"
            f" patterns with rules"
        )
    except Exception as e:
        typer.echo(f"Error exporting configuration: {str(e)}", err=True)
        raise typer.Exit(1)


@app.command()
def show_schema():
    """Display the expected YAML schema"""
    schema = """
version: 0.0.1
kind: acquisition
metadata:
  uda_id: <downloader-name>              # Required
  uda_shard_id: 1                        # Required, default: 1
  org_id: <org-id>                       # Required
spec:
  deploy:
    mode: CONTINUOUS|SCHEDULED           # Required, default: SCHEDULED
    frequency: "*/30 * * * *"  # Required, validated cron for SCHEDULED
  source:
    type: FTP|SFTP|S3|GCS                # Required
    key: <secret-key>                    # Required
  detect:
    - type: "INCLUDE"                    # At least one INCLUDE required
      folder_pattern: "/"                # Required, default: /
      file_pattern: "*.csv"              # Required
      vendor_date_regex: "..."           # Optional, validated if provided
      validations:                       # Optional
        - folder: "/path"                # Required if validations present
          pattern: "*.csv"               # Required if validations present
          file_count:
            z_score: 0.05
          size:
            z_score: 0.05
    - type: "EXCLUDE"                    # Optional
      folder_pattern: "/"                # Required, default: /
      file_pattern: "*.pdf"              # Required
      vendor_date_regex: null            # Not needed for EXCLUDE
      validations: null                  # Not needed for EXCLUDE
  sink:
    type: GCS                            # Required, default: GCS
    bucket_name: <bucket-name>           # Required
    prefix_path: <prefix>                # Required
    retention_policy: {}                 # Optional
"""
    typer.echo(schema)


if __name__ == "__main__":
    app()
