import os
import json
import requests
from typing import Optional
from blocks import config
from blocks_control_sdk.constants.core import WORKSPACE_DIR
from blocks_control_sdk.utils import get_blocks_runtime_config, patch_blocks_runtime_config, BlocksRuntimeConfigKeys, remove_newlines_and_special_characters
from blocks_control_sdk.tools.blocks import get_task_header
from blocks_control_sdk.clients.api import Chat


def get_new_gitlab_token():
    """Get GitLab token from runtime config or environment."""
    blocks_runtime_config = get_blocks_runtime_config()
    return blocks_runtime_config.get("GITLAB_TOKEN", os.getenv("GITLAB_TOKEN"))


def get_gitlab_base_url():
    """Get GitLab base URL from runtime config or environment, defaults to gitlab.com."""
    blocks_runtime_config = get_blocks_runtime_config()
    return blocks_runtime_config.get("GITLAB_BASE_URL", os.getenv("GITLAB_BASE_URL", "https://gitlab.com"))


# Cache for authentication method
_auth_method_cache = {}

def get_gitlab_auth_headers(token: str = None):
    """
    Get the appropriate authentication headers for GitLab API.

    Automatically detects whether to use Bearer token or PRIVATE-TOKEN authentication.

    Args:
        token: GitLab token (optional, will use get_new_gitlab_token() if not provided)

    Returns:
        Dictionary with appropriate authentication headers
    """
    if not token:
        token = get_new_gitlab_token()

    if not token:
        return {}

    # Check cache first
    if token in _auth_method_cache:
        return _auth_method_cache[token].copy()

    gitlab_base_url = get_gitlab_base_url()

    # Try Bearer token first (OAuth2 style)
    test_headers = [
        {"Authorization": f"Bearer {token}"},
        {"PRIVATE-TOKEN": token},
    ]

    # Test which authentication method works
    for headers in test_headers:
        try:
            test_url = f"{gitlab_base_url}/api/v4/user"
            response = requests.get(test_url, headers=headers, timeout=5)
            if response.status_code == 200:
                print(f"Detected authentication method: {list(headers.keys())[0]}")
                _auth_method_cache[token] = headers
                return headers.copy()
        except Exception as e:
            print(f"Auth test failed for {list(headers.keys())[0]}: {e}")
            continue

    # Default to PRIVATE-TOKEN if no method validated
    print("Warning: Could not validate token, defaulting to PRIVATE-TOKEN authentication")
    default_headers = {"PRIVATE-TOKEN": token}
    _auth_method_cache[token] = default_headers
    return default_headers.copy()


def create_gitlab_issue_comment(project_path: str, issue_iid: int, body: str):
    """
    Create a comment on a GitLab issue.

    Args:
        project_path: The project path (e.g., "group/project")
        issue_iid: The issue IID (internal ID)
        body: The comment body

    Returns:
        The created comment data
    """
    print("="*100)
    print(f"TOOL CALL: Creating GitLab issue comment on issue #{issue_iid}")
    print("="*100)

    try:
        gitlab_token = get_new_gitlab_token()
        if not gitlab_token:
            error_msg = "Error: GITLAB_TOKEN not available"
            print(error_msg)
            return {"error": error_msg}

        gitlab_base_url = get_gitlab_base_url()

        # URL-encode the project path
        encoded_project_path = requests.utils.quote(project_path, safe='')

        url = f"{gitlab_base_url}/api/v4/projects/{encoded_project_path}/issues/{issue_iid}/notes"

        headers = {
            "Authorization": f"Bearer {gitlab_token}",
            "Content-Type": "application/json"
        }

        data = {
            "body": body
        }

        response = requests.post(
            url,
            headers=headers,
            json=data,
            timeout=30
        )

        if response.status_code == 201:
            comment_data = response.json()
            print(f"Successfully created comment with ID: {comment_data.get('id')}")
            return comment_data
        else:
            error_msg = f"Error creating GitLab issue comment: HTTP {response.status_code} - {response.text}"
            print(error_msg)
            return {"error": error_msg}

    except Exception as e:
        error_msg = f"Error creating GitLab issue comment: {str(e)}"
        print(error_msg)
        return {"error": error_msg}


def create_gitlab_merge_request_comment(project_path: str, mr_iid: int, body: str):
    """
    Create a comment on a GitLab merge request.

    Args:
        project_path: The project path (e.g., "group/project")
        mr_iid: The merge request IID (internal ID)
        body: The comment body

    Returns:
        The created comment data
    """
    print("="*100)
    print(f"TOOL CALL: Creating GitLab merge request comment on MR !{mr_iid}")
    print("="*100)

    try:
        gitlab_token = get_new_gitlab_token()
        if not gitlab_token:
            error_msg = "Error: GITLAB_TOKEN not available"
            print(error_msg)
            return {"error": error_msg}

        gitlab_base_url = get_gitlab_base_url()

        # URL-encode the project path
        encoded_project_path = requests.utils.quote(project_path, safe='')

        url = f"{gitlab_base_url}/api/v4/projects/{encoded_project_path}/merge_requests/{mr_iid}/notes"

        headers = {
            "Authorization": f"Bearer {gitlab_token}",
            "Content-Type": "application/json"
        }

        data = {
            "body": body
        }

        response = requests.post(
            url,
            headers=headers,
            json=data,
            timeout=30
        )

        if response.status_code == 201:
            comment_data = response.json()
            print(f"Successfully created comment with ID: {comment_data.get('id')}")
            return comment_data
        else:
            error_msg = f"Error creating GitLab merge request comment: HTTP {response.status_code} - {response.text}"
            print(error_msg)
            return {"error": error_msg}

    except Exception as e:
        error_msg = f"Error creating GitLab merge request comment: {str(e)}"
        print(error_msg)
        return {"error": error_msg}


def create_gitlab_issue_discussion_reply(project_path: str, issue_iid: int, discussion_id: str, body: str):
    """
    Create a reply to an existing GitLab issue discussion (threaded comment).

    Args:
        project_path: The project path (e.g., "group/project")
        issue_iid: The issue IID (internal ID)
        discussion_id: The discussion ID to reply to
        body: The comment body

    Returns:
        The created comment data
    """
    print("="*100)
    print(f"TOOL CALL: Creating GitLab issue discussion reply on issue #{issue_iid}, discussion {discussion_id}")
    print("="*100)

    try:
        gitlab_token = get_new_gitlab_token()
        if not gitlab_token:
            error_msg = "Error: GITLAB_TOKEN not available"
            print(error_msg)
            return {"error": error_msg}

        gitlab_base_url = get_gitlab_base_url()

        # URL-encode the project path
        encoded_project_path = requests.utils.quote(project_path, safe='')

        url = f"{gitlab_base_url}/api/v4/projects/{encoded_project_path}/issues/{issue_iid}/discussions/{discussion_id}/notes"

        headers = {
            "Authorization": f"Bearer {gitlab_token}",
            "Content-Type": "application/json"
        }

        data = {
            "body": body
        }

        response = requests.post(
            url,
            headers=headers,
            json=data,
            timeout=30
        )

        if response.status_code == 201:
            comment_data = response.json()
            print(f"Successfully created threaded reply with ID: {comment_data.get('id')}")
            return comment_data
        else:
            error_msg = f"Error creating GitLab issue discussion reply: HTTP {response.status_code} - {response.text}"
            print(error_msg)
            return {"error": error_msg}

    except Exception as e:
        error_msg = f"Error creating GitLab issue discussion reply: {str(e)}"
        print(error_msg)
        return {"error": error_msg}


def create_gitlab_merge_request_discussion_reply(project_path: str, mr_iid: int, discussion_id: str, body: str):
    """
    Create a reply to an existing GitLab merge request discussion (threaded comment).

    Args:
        project_path: The project path (e.g., "group/project")
        mr_iid: The merge request IID (internal ID)
        discussion_id: The discussion ID to reply to
        body: The comment body

    Returns:
        The created comment data
    """
    print("="*100)
    print(f"TOOL CALL: Creating GitLab merge request discussion reply on MR !{mr_iid}, discussion {discussion_id}")
    print("="*100)

    try:
        gitlab_token = get_new_gitlab_token()
        if not gitlab_token:
            error_msg = "Error: GITLAB_TOKEN not available"
            print(error_msg)
            return {"error": error_msg}

        gitlab_base_url = get_gitlab_base_url()

        # URL-encode the project path
        encoded_project_path = requests.utils.quote(project_path, safe='')

        url = f"{gitlab_base_url}/api/v4/projects/{encoded_project_path}/merge_requests/{mr_iid}/discussions/{discussion_id}/notes"

        headers = {
            "Authorization": f"Bearer {gitlab_token}",
            "Content-Type": "application/json"
        }

        data = {
            "body": body
        }

        response = requests.post(
            url,
            headers=headers,
            json=data,
            timeout=30
        )

        if response.status_code == 201:
            comment_data = response.json()
            print(f"Successfully created threaded reply with ID: {comment_data.get('id')}")
            return comment_data
        else:
            error_msg = f"Error creating GitLab merge request discussion reply: HTTP {response.status_code} - {response.text}"
            print(error_msg)
            return {"error": error_msg}

    except Exception as e:
        error_msg = f"Error creating GitLab merge request discussion reply: {str(e)}"
        print(error_msg)
        return {"error": error_msg}


def update_gitlab_issue_comment(project_path: str, issue_iid: int, comment_id: int, body: str):
    """
    Update a comment on a GitLab issue.

    Args:
        project_path: The project path (e.g., "group/project")
        issue_iid: The issue IID (internal ID)
        comment_id: The comment ID to update
        body: The new comment body

    Returns:
        The updated comment data
    """
    print("="*100)
    print(f"TOOL CALL: Updating GitLab issue comment {comment_id} on issue #{issue_iid}")
    print("="*100)

    try:
        gitlab_token = get_new_gitlab_token()
        if not gitlab_token:
            error_msg = "Error: GITLAB_TOKEN not available"
            print(error_msg)
            return {"error": error_msg}

        gitlab_base_url = get_gitlab_base_url()

        # URL-encode the project path
        encoded_project_path = requests.utils.quote(project_path, safe='')

        url = f"{gitlab_base_url}/api/v4/projects/{encoded_project_path}/issues/{issue_iid}/notes/{comment_id}"

        headers = {
            "Authorization": f"Bearer {gitlab_token}",
            "Content-Type": "application/json"
        }

        data = {
            "body": body
        }

        response = requests.put(
            url,
            headers=headers,
            json=data,
            timeout=30
        )

        if response.status_code == 200:
            comment_data = response.json()
            print(f"Successfully updated comment with ID: {comment_data.get('id')}")
            return comment_data
        else:
            error_msg = f"Error updating GitLab issue comment: HTTP {response.status_code} - {response.text}"
            print(error_msg)
            return {"error": error_msg}

    except Exception as e:
        error_msg = f"Error updating GitLab issue comment: {str(e)}"
        print(error_msg)
        return {"error": error_msg}


def update_gitlab_merge_request_comment(project_path: str, mr_iid: int, comment_id: int, body: str):
    """
    Update a comment on a GitLab merge request.

    Args:
        project_path: The project path (e.g., "group/project")
        mr_iid: The merge request IID (internal ID)
        comment_id: The comment ID to update
        body: The new comment body

    Returns:
        The updated comment data
    """
    print("="*100)
    print(f"TOOL CALL: Updating GitLab merge request comment {comment_id} on MR !{mr_iid}")
    print("="*100)

    try:
        gitlab_token = get_new_gitlab_token()
        if not gitlab_token:
            error_msg = "Error: GITLAB_TOKEN not available"
            print(error_msg)
            return {"error": error_msg}

        gitlab_base_url = get_gitlab_base_url()

        # URL-encode the project path
        encoded_project_path = requests.utils.quote(project_path, safe='')

        url = f"{gitlab_base_url}/api/v4/projects/{encoded_project_path}/merge_requests/{mr_iid}/notes/{comment_id}"

        headers = {
            "Authorization": f"Bearer {gitlab_token}",
            "Content-Type": "application/json"
        }

        data = {
            "body": body
        }

        response = requests.put(
            url,
            headers=headers,
            json=data,
            timeout=30
        )

        if response.status_code == 200:
            comment_data = response.json()
            print(f"Successfully updated comment with ID: {comment_data.get('id')}")
            return comment_data
        else:
            error_msg = f"Error updating GitLab merge request comment: HTTP {response.status_code} - {response.text}"
            print(error_msg)
            return {"error": error_msg}

    except Exception as e:
        error_msg = f"Error updating GitLab merge request comment: {str(e)}"
        print(error_msg)
        return {"error": error_msg}


def update_gitlab_issue_comment_with_header(project_path: str, issue_iid: int, comment_id: int, body: str = ""):
    """
    Update a GitLab issue comment with a header.

    Args:
        project_path: The project path (e.g., "group/project")
        issue_iid: The issue IID (internal ID)
        comment_id: The comment ID to update
        body: Optional additional body text

    Returns:
        The comment ID
    """
    print("="*100)
    print(f"TOOL CALL: Updating GitLab issue comment with header: {comment_id}")
    print("="*100)

    issue_body_header = get_task_header()
    if body:
        # strip newlines
        body = remove_newlines_and_special_characters(body)
        issue_body_header += f"\n> {body}"

    result = update_gitlab_issue_comment(project_path, issue_iid, comment_id, issue_body_header)
    return result.get("id") if isinstance(result, dict) else None


def update_gitlab_merge_request_comment_with_header(project_path: str, mr_iid: int, comment_id: int, body: str = ""):
    """
    Update a GitLab merge request comment with a header.

    Args:
        project_path: The project path (e.g., "group/project")
        mr_iid: The merge request IID (internal ID)
        comment_id: The comment ID to update
        body: Optional additional body text

    Returns:
        The comment ID
    """
    print("="*100)
    print(f"TOOL CALL: Updating GitLab merge request comment with header: {comment_id}")
    print("="*100)

    issue_body_header = get_task_header()
    if body:
        # strip newlines
        body = remove_newlines_and_special_characters(body)
        issue_body_header += f"\n> {body}"

    result = update_gitlab_merge_request_comment(project_path, mr_iid, comment_id, issue_body_header)
    return result.get("id") if isinstance(result, dict) else None


def clone_gitlab_repository_into_folder(project_path: str, folder_name: str, ref: str = "main") -> str:
    """
    Clone a GitLab repository into a folder.

    Args:
        project_path: The project path (e.g., "group/project")
        folder_name: The folder name to clone into
        ref: The branch or ref to checkout (default: "main")

    Returns:
        Success or error message
    """
    try:
        full_path = (WORKSPACE_DIR / folder_name).absolute()
        print("="*100)
        print(f"TOOL CALL: Cloning GitLab repository into folder: {project_path}, {full_path} @ {ref}")
        print("="*100)

        gitlab_token = get_new_gitlab_token()
        gitlab_base_url = get_gitlab_base_url()

        if gitlab_token:
            # Use authenticated clone URL
            gitlab_repository_url = f"https://oauth2:{gitlab_token}@{gitlab_base_url.replace('https://', '')}/{project_path}.git"
        else:
            # Fall back to public clone URL
            gitlab_repository_url = f"{gitlab_base_url}/{project_path}.git"

        print(f"Performing tool call: Cloning {project_path}... {full_path}")

        import subprocess
        # Use git command directly for better control
        cmd = ["git", "clone", gitlab_repository_url, str(full_path)]
        result = subprocess.run(cmd, capture_output=True, text=True)

        if result.returncode != 0:
            raise Exception(f"Git clone failed: {result.stderr}")

        # Checkout specific ref if not main/master
        if ref and ref not in ["main", "master"]:
            cmd = ["git", "checkout", ref]
            result = subprocess.run(cmd, cwd=full_path, capture_output=True, text=True)
            if result.returncode != 0:
                raise Exception(f"Git checkout failed: {result.stderr}")

        return f"Cloned GitLab repository into folder: {full_path}"
    except Exception as e:
        print(f"Error cloning GitLab repository: {e}")
        return f"Error cloning GitLab repository: {e}"