"""
Salesforce documentation generation helpers.
"""

from __future__ import annotations

from datetime import datetime, timezone
from pathlib import Path
from typing import Any, Dict, List

from g_gremlin import sfdc as sfdc_helpers
from g_gremlin.sfdc_metadata_components import MetadataType, scan_source_dir


def generate_docs(
    *,
    source_dir: Path,
    objects: List[str],
    org_alias: str | None = None,
) -> Dict[str, Any]:
    inventory = scan_source_dir(source_dir)
    identity = _safe_org_identity(org_alias)

    object_names = [obj for obj in objects if obj]
    if not object_names:
        object_names = _infer_object_names(inventory)

    object_sections: List[Dict[str, Any]] = []
    for object_name in object_names:
        describe_result: Dict[str, Any] = {}
        describe_error = ""
        try:
            describe_result = sfdc_helpers.describe_sobject(object_name, org_alias=org_alias).get("result") or {}
        except Exception as exc:
            describe_error = str(exc)
        object_sections.append(
            {
                "object": object_name,
                "describe": describe_result,
                "describe_error": describe_error,
                "record_types": _component_names_for_object(inventory, MetadataType.RECORD_TYPE, object_name),
                "validation_rules": _component_names_for_object(
                    inventory, MetadataType.VALIDATION_RULE, object_name
                ),
                "layouts": _component_names_for_object(inventory, MetadataType.LAYOUT, object_name),
            }
        )

    markdown = _render_markdown(
        identity=identity,
        object_sections=object_sections,
        permission_sets=_component_names(inventory, MetadataType.PERMISSION_SET),
        queues=_component_names(inventory, MetadataType.QUEUE),
        flows=_component_names(inventory, MetadataType.FLOW),
        generated_at=datetime.now(timezone.utc),
    )

    by_type = inventory.by_type()
    type_counts = {type_name: len(comps) for type_name, comps in sorted(by_type.items())}
    return {
        "markdown": markdown,
        "summary": {
            "generated_at": datetime.now(timezone.utc).isoformat(),
            "source_dir": str(source_dir),
            "object_count": len(object_sections),
            "component_count": len(inventory.components),
            "type_counts": type_counts,
        },
        "identity": identity,
        "objects": object_sections,
    }


def _safe_org_identity(org_alias: str | None) -> Dict[str, Any]:
    try:
        return sfdc_helpers.get_org_identity(org_alias=org_alias)
    except Exception as exc:
        return {"error": str(exc)}


def _infer_object_names(inventory) -> List[str]:
    names = {
        comp.parent_object
        for comp in inventory.components
        if comp.parent_object and comp.type in {MetadataType.CUSTOM_FIELD, MetadataType.RECORD_TYPE}
    }
    return sorted(names)


def _component_names(inventory, metadata_type: MetadataType) -> List[str]:
    return sorted(comp.api_name for comp in inventory.components if comp.type is metadata_type)


def _component_names_for_object(inventory, metadata_type: MetadataType, object_name: str) -> List[str]:
    prefix = f"{object_name}."
    names = [
        comp.api_name.split(".", 1)[1]
        for comp in inventory.components
        if comp.type is metadata_type and comp.api_name.startswith(prefix)
    ]
    return sorted(names)


def _render_markdown(
    *,
    identity: Dict[str, Any],
    object_sections: List[Dict[str, Any]],
    permission_sets: List[str],
    queues: List[str],
    flows: List[str],
    generated_at: datetime,
) -> str:
    lines: List[str] = []
    lines.append("# Salesforce Org Documentation")
    lines.append("")
    lines.append(f"Generated: {generated_at.isoformat()}")
    lines.append("")
    lines.append("## Org Identity")
    lines.append("")
    if identity.get("error"):
        lines.append(f"- Error: {identity.get('error')}")
    else:
        lines.append(f"- Alias: {identity.get('alias') or 'n/a'}")
        lines.append(f"- Username: {identity.get('username') or 'n/a'}")
        lines.append(f"- Org ID: {identity.get('org_id') or 'n/a'}")
        lines.append(f"- Instance URL: {identity.get('instance_url') or 'n/a'}")
    lines.append("")
    lines.append("## Objects")
    lines.append("")
    if not object_sections:
        lines.append("_No objects selected or inferred._")
        lines.append("")
    for section in object_sections:
        object_name = section.get("object") or "Unknown"
        lines.append(f"### {object_name}")
        lines.append("")
        if section.get("describe_error"):
            lines.append(f"- Describe error: {section.get('describe_error')}")
            lines.append("")
        describe = section.get("describe") or {}
        fields = list(describe.get("fields") or [])
        if fields:
            lines.append(f"- Label: {describe.get('label') or object_name}")
            lines.append(f"- Field count: {len(fields)}")
            lines.append("")
            lines.append("| Field | Type | Updateable | Label |")
            lines.append("|---|---|---|---|")
            for field in fields:
                lines.append(
                    f"| {field.get('name', '')} | {field.get('type', '')} | "
                    f"{'yes' if field.get('updateable') else 'no'} | {field.get('label', '')} |"
                )
            lines.append("")
        _append_list(lines, "Record Types", section.get("record_types") or [])
        _append_list(lines, "Validation Rules", section.get("validation_rules") or [])
        _append_list(lines, "Layouts", section.get("layouts") or [])

    lines.append("## Permission Sets")
    lines.append("")
    _append_items(lines, permission_sets)
    lines.append("## Queues")
    lines.append("")
    _append_items(lines, queues)
    lines.append("## Flows")
    lines.append("")
    _append_items(lines, flows)

    text = "\n".join(lines).rstrip()
    if not text.endswith("\n"):
        text += "\n"
    return text


def _append_list(lines: List[str], title: str, items: List[str]) -> None:
    lines.append(f"#### {title}")
    lines.append("")
    _append_items(lines, items)


def _append_items(lines: List[str], items: List[str]) -> None:
    if not items:
        lines.append("_None_")
        lines.append("")
        return
    for item in items:
        lines.append(f"- {item}")
    lines.append("")
