import json
import os
import re
import sys

import requests

DEFAULTS = {
    "typeOfReport": "pytest",
    "testEnv": "Not Given",
    "testType": "Not Given",
    "tags": [],
    "browser": "Not Given",
    "browserVersion": "Not Given",
    "iterationId": "Batch1",
    "baseUrl": "http://localhost:4000/api/v1/public",
}


def normalize_tags(tags):
    """
    Supports:
    ["smoke"]
    ["smoke,regression"]
    ["@smoke and @wallet"]
    ["@smoke or @wallet"]
    """
    if not tags:
        return []

    normalized = []

    for raw in tags:
        raw = str(raw).lower()
        raw = raw.replace("(", "").replace(")", "")
        raw = re.sub(r"\s+(and|or)\s+", ",", raw)

        parts = re.split(r"[,\s]+", raw)

        for part in parts:
            part = part.strip()

            if not part:
                continue

            if part.startswith("@"):
                part = part[1:]

            normalized.append(part)

    # remove duplicates while keeping order
    seen = set()
    result = []

    for item in normalized:
        if item not in seen:
            seen.add(item)
            result.append(item)

    return result


def get_auth_token(config):
    # 1. token from config.json
    token = config.get("token")
    if token:
        return token

    # 2. token from env variable
    env_token = os.getenv("DASHBOARD_TOKEN")
    if env_token:
        return env_token

    # 3. optional login using email/password if you want later
    email = config.get("email")
    password = config.get("password")

    if email and password:
        base_url = config.get("baseUrl", DEFAULTS["baseUrl"])
        login_url = f"{base_url}/auth/login"

        response = requests.post(
            login_url,
            json={
                "email": email,
                "password": password,
            },
            timeout=20,
        )

        response.raise_for_status()

        data = response.json()

        if "token" not in data:
            raise RuntimeError("Login API did not return token")

        return data["token"]

    raise RuntimeError(
        "No token found. Add 'token' in config.json or set DASHBOARD_TOKEN"
    )


def get_endpoint(base_url, report_type, project_id):
    mapping = {
        "pytest": f"{base_url}/iterations/pyTest/{project_id}",
        "cucumber": f"{base_url}/iterations/cucumber/{project_id}",
        "newman": f"{base_url}/iterations/newman/{project_id}",
        "normal": f"{base_url}/iterations/{project_id}",
    }

    if report_type not in mapping:
        raise ValueError(
            f"Invalid typeOfReport '{report_type}'. "
            f"Allowed: {', '.join(mapping.keys())}"
        )

    return mapping[report_type]


def publish_report(config):
    """
    Called from:
        11automation start
    Reads all values from 11automation/config.json
    """

    report_type = config.get("typeOfReport", DEFAULTS["typeOfReport"])
    test_env = config.get("testEnv", DEFAULTS["testEnv"])
    test_type = config.get("testType", DEFAULTS["testType"])
    browser = config.get("browser", DEFAULTS["browser"])
    browser_version = config.get("browserVersion", DEFAULTS["browserVersion"])
    iteration_id = config.get("iterationId", DEFAULTS["iterationId"])
    tags = normalize_tags(config.get("tags", DEFAULTS["tags"]))

    project_id = config.get("projectId")
    report_file = config.get("file")
    base_url = config.get("baseUrl", DEFAULTS["baseUrl"])

    if not project_id:
        raise RuntimeError("Missing 'projectId' in config.json")

    if not report_file:
        raise RuntimeError("Missing 'file' in config.json")

    if not os.path.exists(report_file):
        raise RuntimeError(f"Report file not found: {report_file}")

    file_size_mb = os.path.getsize(report_file) / (1024 * 1024)
    is_large_file = file_size_mb > 32

    token = get_auth_token(config)
    endpoint = get_endpoint(base_url, report_type, project_id)

    payload = {
        "iterationId": iteration_id,
        "testType": test_type,
        "testEnvironment": test_env,
        "browserUsed": browser,
        "browserVersion": browser_version,
        "tags[]": tags,
    }

    headers = {
        "Authorization": f"Bearer {token}",
    }

    print("\nPublishing report...")
    print(f"Type: {report_type}")
    print(f"Project: {project_id}")
    print(f"File: {report_file}")
    print(f"Endpoint: {endpoint}")

    if is_large_file:
        print(f"\nLarge file detected ({file_size_mb:.2f}MB). Using Secure Large File Upload...")
        
        print("  - Step 1: Getting secure upload link...")
        init_url = f"{base_url}/iterations/upload/init"
        init_resp = requests.post(
            init_url, 
            json={"fileName": os.path.basename(report_file), "projectId": project_id}, 
            headers=headers, 
            timeout=30
        )
        
        if init_resp.status_code >= 300:
            print("\nFailed to initialize large file upload")
            print(f"Status Code: {init_resp.status_code}")
            print(init_resp.text)
            sys.exit(1)
            
        init_data = init_resp.json()
        upload_url = init_data.get("uploadUrl")
        file_path_cloud = init_data.get("filePath")
        
        if not upload_url or not file_path_cloud:
            print("\nInvalid response from upload initialization (missing uploadUrl or filePath)")
            sys.exit(1)
            
        print("  - Step 2: Uploading file to cloud storage (this might take a while)...")
        with open(report_file, "rb") as f:
            put_resp = requests.put(upload_url, data=f, headers={"Content-Type": "application/json"}, timeout=600)
            
        if put_resp.status_code >= 300:
            print("\nFailed to upload file to cloud storage")
            print(f"Status Code: {put_resp.status_code}")
            print(put_resp.text)
            sys.exit(1)
            
        print("  - Step 3: Instructing server to process report...")
        payload["uploadType"] = "cloud-storage"
        payload["filePath"] = file_path_cloud
        
        print("Payload:")
        print(json.dumps(payload, indent=2))
        
        response = requests.post(
            endpoint,
            headers=headers,
            json=payload,
            timeout=120,
        )
        
    else:
        print("Payload:")
        print(json.dumps(payload, indent=2))
        
        with open(report_file, "rb") as report:
            files = {
                "files": (
                    os.path.basename(report_file),
                    report,
                    "application/json",
                )
            }
            
            response = requests.post(
                endpoint,
                headers=headers,
                data=payload,
                files=files,
                timeout=60,
            )

    if response.status_code >= 300:
        print("\n Upload failed")
        print(f"Status Code: {response.status_code}")
        print(response.text)
        sys.exit(1)

    print("\nReport uploaded successfully")
    print(response.text)