"""Azure Data Factory (ADF) processor for extracting and analyzing metadata."""

import json
from typing import Any, Dict, List, Optional


def extract_adf_metadata(json_objects: List[Dict[str, Any]]) -> Dict[str, Any]:
    """Extract normalized ADF metadata from parsed JSON objects.

    Builds a comprehensive metadata dictionary from ADF documents:
    - pipelines: list of {name, activity_count, activity_types}
    - datasets: list of {name, type}
    - linked_services: list of {name, type}
    - parameters: list of {name, type, default_value}
    - folders: list of folder names (if present)

    Args:
        json_objects: List of dicts from find_adf_json_files() with 'content' key

    Returns:
        Normalized metadata dictionary with empty lists for missing components
    """
    metadata: Dict[str, Any] = {
        "pipelines": [],
        "datasets": [],
        "linked_services": [],
        "parameters": [],
        "folders": [],
        "source_type": None,
    }

    for obj_wrapper in json_objects:
        json_obj = obj_wrapper.get("content", {})
        source_type = obj_wrapper.get("type", "unknown")

        if metadata["source_type"] is None:
            metadata["source_type"] = source_type

        # Case 1: ARM template
        if source_type == "arm_template":
            _extract_from_arm_template(json_obj, metadata)

        # Case 2: Repo export (pipeline, dataset, linkedService files already scanned)
        elif source_type == "repo_export":
            _extract_from_repo_export(json_obj, metadata)

        # Case 3: Single JSON from ZIP or direct upload
        elif source_type == "single_json" or source_type == "json_upload":
            _extract_from_generic(json_obj, metadata)

        # Case 4: Fallback for any other generic format
        else:
            _extract_from_generic(json_obj, metadata)

    return metadata


def _extract_from_arm_template(arm_template: Dict[str, Any], metadata: Dict[str, Any]) -> None:
    """Extract ADF components from ARM template format."""
    resources = arm_template.get("resources", [])

    for resource in resources:
        if not isinstance(resource, dict):
            continue

        res_type = resource.get("type", "")

        # Pipeline
        if "pipelines" in res_type.lower():
            pipeline_props = resource.get("properties", {})
            pipeline_meta = _parse_pipeline(pipeline_props, resource.get("name", "unknown"))
            metadata["pipelines"].append(pipeline_meta)

        # Dataset
        elif "datasets" in res_type.lower():
            dataset_meta = _parse_dataset(resource.get("properties", {}), resource.get("name", "unknown"))
            metadata["datasets"].append(dataset_meta)

        # Linked Service
        elif "linkedservices" in res_type.lower():
            service_meta = _parse_linked_service(resource.get("properties", {}), resource.get("name", "unknown"))
            metadata["linked_services"].append(service_meta)

    # Parameters
    params = arm_template.get("parameters", {})
    for param_name, param_obj in params.items():
        if isinstance(param_obj, dict):
            metadata["parameters"].append({
                "name": param_name,
                "type": param_obj.get("type", "unknown"),
                "default_value": param_obj.get("defaultValue", None)
            })


def _extract_from_repo_export(json_obj: Dict[str, Any], metadata: Dict[str, Any]) -> None:
    """Extract ADF components from repo export JSON files (individual files per pipeline/dataset)."""
    obj_type = json_obj.get("type", "")
    properties = json_obj.get("properties", {})
    props_type = properties.get("type", "")
    
    # Check for linked services FIRST (most specific)
    # Linked services have type like "SqlServer", "AzureBlobStorage" in properties.type
    if props_type in ["SqlServer", "AzureBlobStorage", "AzureStorage", "Databricks", "AzureSqlDatabase", 
                      "AzureDataLakeStore", "CosmosDb", "HttpServer", "RestService"]:
        service_meta = _parse_linked_service(properties, json_obj.get("name", "unknown"))
        if service_meta not in metadata["linked_services"]:
            metadata["linked_services"].append(service_meta)
    
    # Check by type field for linked services
    elif "linkedservice" in obj_type.lower() or "Microsoft.DataFactory/factories/linkedServices" in obj_type:
        service_meta = _parse_linked_service(properties, json_obj.get("name", "unknown"))
        if service_meta not in metadata["linked_services"]:
            metadata["linked_services"].append(service_meta)
    
    # Check structure: if has activities in properties, it's a pipeline
    elif "activities" in properties:
        pipeline_meta = _parse_pipeline(properties, json_obj.get("name", "unknown"))
        if pipeline_meta not in metadata["pipelines"]:
            metadata["pipelines"].append(pipeline_meta)
    
    # Check by type field for pipelines
    elif "pipeline" in obj_type.lower() or "Microsoft.DataFactory/factories/pipelines" in obj_type:
        pipeline_meta = _parse_pipeline(properties, json_obj.get("name", "unknown"))
        if pipeline_meta not in metadata["pipelines"]:
            metadata["pipelines"].append(pipeline_meta)

    # Check structure: if has linkedServiceName or typeProperties, it's a dataset
    elif "linkedServiceName" in properties or "typeProperties" in properties:
        dataset_meta = _parse_dataset(properties, json_obj.get("name", "unknown"))
        if dataset_meta not in metadata["datasets"]:
            metadata["datasets"].append(dataset_meta)
    
    # Check by type field for datasets
    elif "dataset" in obj_type.lower() or "Microsoft.DataFactory/factories/datasets" in obj_type:
        dataset_meta = _parse_dataset(properties, json_obj.get("name", "unknown"))
        if dataset_meta not in metadata["datasets"]:
            metadata["datasets"].append(dataset_meta)


def _extract_from_generic(json_obj: Dict[str, Any], metadata: Dict[str, Any]) -> None:
    """Extract ADF components from a generic/standalone JSON file."""
    obj_type = json_obj.get("type", "")
    properties = json_obj.get("properties", {})

    # If it looks like a pipeline (has activities in properties)
    if "activities" in properties or ("activities" in json_obj and json_obj.get("type", "").lower().find("pipeline") >= 0):
        pipeline_meta = _parse_pipeline(properties if "activities" in properties else json_obj, json_obj.get("name", "unknown"))
        if pipeline_meta not in metadata["pipelines"]:
            metadata["pipelines"].append(pipeline_meta)

    # If it looks like a dataset
    elif "dataset" in obj_type.lower():
        dataset_meta = _parse_dataset(properties, json_obj.get("name", "unknown"))
        if dataset_meta not in metadata["datasets"]:
            metadata["datasets"].append(dataset_meta)

    # If it looks like a linked service
    elif "linkedservice" in obj_type.lower() or "linkedServices" in obj_type:
        service_meta = _parse_linked_service(properties, json_obj.get("name", "unknown"))
        if service_meta not in metadata["linked_services"]:
            metadata["linked_services"].append(service_meta)


def _parse_pipeline(pipeline_props: Dict[str, Any], name: str) -> Dict[str, Any]:
    """Parse a pipeline object to extract name, activities, and activity types."""
    activities = pipeline_props.get("activities", []) if isinstance(pipeline_props, dict) else []

    activity_types = []
    if isinstance(activities, list):
        for activity in activities:
            if isinstance(activity, dict):
                activity_type = activity.get("type", "UnknownActivity")
                if activity_type not in activity_types:
                    activity_types.append(activity_type)

    return {
        "name": name,
        "activity_count": len(activities),
        "activity_types": activity_types
    }


def _parse_dataset(dataset_props: Dict[str, Any], name: str) -> Dict[str, Any]:
    """Parse a dataset object."""
    dataset_type = dataset_props.get("type", "Unknown") if isinstance(dataset_props, dict) else "Unknown"
    return {
        "name": name,
        "type": dataset_type
    }


def _parse_linked_service(service_props: Dict[str, Any], name: str) -> Dict[str, Any]:
    """Parse a linked service object."""
    service_type = service_props.get("type", "Unknown") if isinstance(service_props, dict) else "Unknown"
    return {
        "name": name,
        "type": service_type
    }


def generate_adf_metadata_report(metadata: Dict[str, Any]) -> str:
    """Format ADF metadata into a readable text report.

    Args:
        metadata: Normalized metadata dictionary from extract_adf_metadata()

    Returns:
        Formatted text report
    """
    lines = []

    lines.append("Azure Data Factory - Metadata Report")
    lines.append("=" * 70)
    lines.append(f"Source Type: {metadata.get('source_type', 'Unknown')}")
    lines.append("")

    # Pipelines
    pipelines = metadata.get("pipelines", [])
    lines.append(f"Pipelines: ({len(pipelines)})")
    for pipeline in pipelines:
        lines.append(f"  - {pipeline['name']}")
        lines.append(f"    Activities: {pipeline['activity_count']}")
        if pipeline['activity_types']:
            lines.append(f"    Types: {', '.join(pipeline['activity_types'])}")
    if not pipelines:
        lines.append("  (none)")
    lines.append("")

    # Datasets
    datasets = metadata.get("datasets", [])
    lines.append(f"Datasets: ({len(datasets)})")
    for dataset in datasets:
        lines.append(f"  - {dataset['name']} (type: {dataset['type']})")
    if not datasets:
        lines.append("  (none)")
    lines.append("")

    # Linked Services
    services = metadata.get("linked_services", [])
    lines.append(f"Linked Services: ({len(services)})")
    for service in services:
        lines.append(f"  - {service['name']} (type: {service['type']})")
    if not services:
        lines.append("  (none)")
    lines.append("")

    # Parameters
    params = metadata.get("parameters", [])
    lines.append(f"Parameters: ({len(params)})")
    for param in params:
        default = f" = {param['default_value']}" if param['default_value'] is not None else ""
        lines.append(f"  - {param['name']} ({param['type']}){default}")
    if not params:
        lines.append("  (none)")
    lines.append("")

    return "\n".join(lines)
