"""Salesforce Developer, GitHub, Confluence, and deploy unit tests."""

from __future__ import annotations

import uuid
from pathlib import Path

import httpx
import pytest

from packages.agents.salesforce_developer import SalesforceDeveloper
from packages.artifacts.design import DesignArtifact, MetadataPlanItem
from packages.config import Settings
from packages.db.artifacts import ArtifactStore
from packages.integrations.git import HttpGitHubClient, MockGitClient, get_git_client, reset_mock_git
from packages.integrations.llm import MockLLMClient
from packages.integrations.salesforce import (
    CliSalesforceDeployer,
    get_salesforce_deployer,
    reset_mock_salesforce,
)
from packages.knowledge.retrievers import (
    HttpConfluenceRetriever,
    _build_confluence_cql,
    _parse_confluence_search_results,
    get_confluence_retriever,
)


@pytest.mark.asyncio
async def test_salesforce_developer_writes_custom_field_stubs(artifacts_tmpdir):
    run_id = uuid.uuid4()
    design = DesignArtifact(
        jira_key="SALES-101",
        summary="Add Customer Tier field",
        approach="Configuration on Account",
        metadata_plan=[
            MetadataPlanItem(
                metadata_type="CustomField",
                api_name="Customer_Tier__c",
                action="create",
                object="Account",
            ),
            MetadataPlanItem(
                metadata_type="ValidationRule",
                api_name="Account_Gold_Validation",
                action="create",
                object="Account",
            ),
            MetadataPlanItem(
                metadata_type="PermissionSet",
                api_name="Account_Field_Access",
                action="create",
            ),
            MetadataPlanItem(
                metadata_type="ApexClass",
                api_name="AccountHandler",
                action="create",
                object="Account",
            ),
        ],
    )
    store = ArtifactStore()
    dev = SalesforceDeveloper(artifacts=store)
    artifact, files = await dev.implement(design, None, run_id)

    assert artifact.files_written
    field_path = "force-app/main/default/objects/Account/fields/Customer_Tier__c.field-meta.xml"
    assert field_path in artifact.files_written
    assert Path(store.run_dir(run_id) / field_path).exists()
    assert "force-app/main/default/classes/AccountHandler.cls" in artifact.files_written
    assert artifact.deferred_types == []
    assert len(files) >= 5


@pytest.mark.asyncio
async def test_salesforce_developer_writes_apex_flow_lwc_stubs(artifacts_tmpdir):
    run_id = uuid.uuid4()
    design = DesignArtifact(
        jira_key="SALES-201",
        summary="Automation stubs",
        approach="Apex + Flow + LWC",
        metadata_plan=[
            MetadataPlanItem(
                metadata_type="ApexTrigger",
                api_name="AccountBeforeSave",
                action="create",
                object="Account",
            ),
            MetadataPlanItem(
                metadata_type="Flow",
                api_name="Account_After_Save",
                action="create",
                object="Account",
            ),
            MetadataPlanItem(
                metadata_type="LightningComponentBundle",
                api_name="accountSummary",
                action="create",
            ),
        ],
    )
    store = ArtifactStore()
    artifact, files = await SalesforceDeveloper(artifacts=store).implement(design, None, run_id)
    assert "force-app/main/default/triggers/AccountBeforeSave.trigger" in files
    assert "force-app/main/default/flows/Account_After_Save.flow-meta.xml" in files
    assert "force-app/main/default/lwc/accountSummary/accountSummary.js" in files
    assert artifact.deferred_types == []


@pytest.mark.asyncio
async def test_mock_git_create_draft_pr(artifacts_tmpdir):
    git = reset_mock_git()
    result = await git.create_draft_pr(
        jira_key="SALES-101",
        branch_name="agent-nova/sales-101",
        commit_message="Agent Nova: SALES-101 — test",
        files={"force-app/main/default/objects/Account/fields/X__c.field-meta.xml": "<xml/>"},
    )
    assert result.branch_name == "agent-nova/sales-101"
    assert result.commit_sha.startswith("mock-")
    assert "pull" in result.pr_url
    assert len(git.calls) == 1


@pytest.mark.asyncio
async def test_http_github_client_creates_draft_pr():
    calls: list[str] = []

    def handler(request: httpx.Request) -> httpx.Response:
        path = request.url.path
        calls.append(f"{request.method} {path}")
        if path.endswith("/git/ref/heads/main") and request.method == "GET":
            return httpx.Response(200, json={"object": {"sha": "base-commit-sha"}})
        if path.endswith("/git/commits/base-commit-sha") and request.method == "GET":
            return httpx.Response(200, json={"tree": {"sha": "base-tree-sha"}})
        if path.endswith("/git/blobs") and request.method == "POST":
            return httpx.Response(201, json={"sha": "blob-sha-1"})
        if path.endswith("/git/trees") and request.method == "POST":
            body = json_body(request)
            assert body["base_tree"] == "base-tree-sha"
            assert body["tree"][0]["path"].startswith("force-app/")
            return httpx.Response(201, json={"sha": "new-tree-sha"})
        if path.endswith("/git/commits") and request.method == "POST":
            return httpx.Response(201, json={"sha": "new-commit-sha"})
        if path.endswith("/git/refs") and request.method == "POST":
            body = json_body(request)
            assert body["ref"] == "refs/heads/agent-nova/sales-101"
            assert body["sha"] == "new-commit-sha"
            return httpx.Response(201, json={"ref": body["ref"]})
        if path.endswith("/pulls") and request.method == "POST":
            body = json_body(request)
            assert body["draft"] is True
            assert body["head"] == "agent-nova/sales-101"
            return httpx.Response(
                201,
                json={"html_url": "https://github.com/acme/sfdx/pull/42", "number": 42},
            )
        return httpx.Response(500, json={"message": f"unexpected {request.method} {path}"})

    settings = Settings(
        git_provider="github",
        git_repo_url="https://github.com/acme/sfdx",
        git_default_branch="main",
        github_token="ghp_test",
        github_api_base_url="https://api.github.com",
    )
    client = HttpGitHubClient(settings, transport=httpx.MockTransport(handler))
    result = await client.create_draft_pr(
        jira_key="SALES-101",
        branch_name="agent-nova/sales-101",
        commit_message="Agent Nova: SALES-101",
        files={
            "force-app/main/default/classes/Foo.cls": "public class Foo {}",
            "implementation-summary.md": "# skip me",
        },
    )
    assert result.commit_sha == "new-commit-sha"
    assert result.pr_url == "https://github.com/acme/sfdx/pull/42"
    assert any("/git/blobs" in c for c in calls)
    assert any("/pulls" in c for c in calls)


def test_get_git_client_uses_github_provider():
    settings = Settings(
        git_provider="github",
        git_repo_url="acme/sfdx",
        github_token="token",
    )
    client = get_git_client(settings)
    assert isinstance(client, HttpGitHubClient)


def test_get_git_client_mock_default():
    settings = Settings(git_provider="mock")
    client = get_git_client(settings)
    assert isinstance(client, MockGitClient)


def test_github_owner_repo_parsing():
    assert Settings(git_repo_url="https://github.com/acme/sfdx.git").github_owner_repo == (
        "acme",
        "sfdx",
    )
    assert Settings(git_repo_url="git@github.com:acme/sfdx.git").github_owner_repo == (
        "acme",
        "sfdx",
    )
    assert Settings(git_repo_url="acme/sfdx").github_owner_repo == ("acme", "sfdx")


@pytest.mark.asyncio
async def test_mock_salesforce_deployer(artifacts_tmpdir):
    deployer = reset_mock_salesforce()
    result = await deployer.deploy_validate(
        source_dir=Path(artifacts_tmpdir) / "force-app",
        jira_key="SALES-101",
    )
    assert result.success is True
    assert result.check_only is True
    assert len(deployer.calls) == 1


def test_get_salesforce_deployer_cli():
    settings = Settings(salesforce_provider="cli", salesforce_org_alias="dev-sandbox")
    assert isinstance(get_salesforce_deployer(settings), CliSalesforceDeployer)


@pytest.mark.asyncio
async def test_cli_deployer_refuses_production_alias_async(tmp_path):
    deployer = CliSalesforceDeployer(
        Settings(salesforce_provider="cli", salesforce_org_alias="production")
    )
    with pytest.raises(ValueError, match="production"):
        await deployer.deploy_validate(source_dir=tmp_path, jira_key="SALES-1")


@pytest.mark.asyncio
async def test_mock_llm_out_of_scope_not_from_boilerplate():
    prompt = """You are the Requirement Analyst for Agent Nova.

Jira key: SALES-101
Summary: Add Customer Tier field on Account

Description:
Add Customer_Tier__c picklist on Account.

Acceptance criteria:
- Customer_Tier__c picklist exists on Account

Comments:
(none)

v1 allowlist: custom fields, validation rules, record-triggered Flows, Apex classes/triggers, LWCs, permission sets.
Out of scope: production deploy, merge, sharing/OWD redesign, destructive deletes.
"""
    artifact = await MockLLMClient().analyze_requirements(prompt)
    assert artifact.is_clear is True
    assert not any("production" in x.lower() for x in artifact.out_of_scope_items)


def test_build_confluence_cql_scopes_spaces():
    cql = _build_confluence_cql("customer tier", ["ENG", "ARCH"])
    assert 'space in ("ENG", "ARCH")' in cql
    assert 'type = page' in cql
    assert 'title ~ "customer tier"' in cql


def test_build_confluence_cql_shortens_long_ticket_text():
    long = (
        "[Agent Nova test] Point5 — Account Industry Picklist "
        + ("extra filler words about sales ops tagging " * 8)
        + "Industry__c on Account Technology Finance Healthcare"
    )
    cql = _build_confluence_cql(long, ["ENG"])
    assert 'space in ("ENG")' in cql
    assert 'title ~ "Account"' in cql or 'title ~ "Industry"' in cql
    assert "extra filler words about sales ops tagging" not in cql
    assert " OR " in cql


def test_parse_confluence_search_results_extracts_snippet_and_url():
    payload = {
        "results": [
            {
                "content": {
                    "id": "123",
                    "title": "Customer Tier Standards",
                    "body": {
                        "storage": {
                            "value": "<p>Use <strong>Customer_Tier__c</strong> on Account.</p>"
                        }
                    },
                    "_links": {"webui": "/spaces/ENG/pages/123/customer-tier-standards"},
                }
            }
        ]
    }
    hits = _parse_confluence_search_results(payload, "https://example.atlassian.net/wiki", limit=5)
    assert len(hits) == 1
    assert hits[0].title == "Customer Tier Standards"
    assert "Customer_Tier__c" in hits[0].snippet
    assert hits[0].url == "https://example.atlassian.net/wiki/spaces/ENG/pages/123/customer-tier-standards"


@pytest.mark.asyncio
async def test_http_confluence_retriever_searches_live_api():
    def handler(request: httpx.Request) -> httpx.Response:
        assert request.url.path.endswith("/rest/api/content/search")
        assert "space in" in request.url.params["cql"]
        assert request.url.params["expand"] == "body.storage"
        return httpx.Response(
            200,
            json={
                "results": [
                    {
                        "id": "456",
                        "title": "Architecture Standards",
                        "body": {
                            "storage": {
                                "value": "<p>Prefer record-triggered Flows for simple automation.</p>"
                            }
                        },
                        "_links": {"webui": "/spaces/ARCH/pages/456/architecture-standards"},
                    }
                ]
            },
        )

    settings = Settings(
        confluence_provider="http",
        confluence_base_url="https://example.atlassian.net/wiki",
        confluence_space_keys="ENG,ARCH",
        confluence_email="me@example.com",
        confluence_api_token="token-123",
    )
    retriever = HttpConfluenceRetriever(settings, transport=httpx.MockTransport(handler))
    hits = await retriever.search("architecture standards", limit=3)
    assert len(hits) == 1
    assert hits[0].source == "confluence"
    assert "record-triggered Flows" in hits[0].snippet


def test_get_confluence_retriever_uses_http_provider():
    settings = Settings(
        confluence_provider="http",
        confluence_base_url="https://example.atlassian.net/wiki",
        confluence_space_keys="ENG",
        confluence_email="me@example.com",
        confluence_api_token="token-123",
    )
    retriever = get_confluence_retriever(settings)
    assert isinstance(retriever, HttpConfluenceRetriever)


def json_body(request: httpx.Request) -> dict:
    import json

    return json.loads(request.content.decode("utf-8"))
