import json
import os
from datetime import datetime
import string
import requests
from .test_results_to_jira import load_third_party_config


def send_slack_message(slack_webhook_url, message_blocks):
    payload = {"blocks": message_blocks}
    response = requests.post(slack_webhook_url, data=json.dumps(
        payload), headers={'Content-Type': 'application/json'}, timeout=30)
    if response.status_code != 200:
        raise ValueError(
            f"Request to Slack returned an error {response.status_code}, the response is:\n{response.text}")
    print("Slack notification sent!")


def _call_openai(system_prompt: str, user_prompt: str) -> str:
    openai_api_url = "https://api.openai.com/v1/chat/completions"
    qe_agent_api_key = os.environ.get('QE_AGENT_API_KEY')

    if not qe_agent_api_key:
        return "AI summary unavailable: QE_AGENT_API_KEY not set"

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

    payload = {"messages": [{"role": "system", "content": system_prompt}, {
        "role": "user", "content": user_prompt}], "model": "gpt-4", "temperature": 0.5, "max_tokens": 2000}

    try:
        response = requests.post(
            openai_api_url, headers=headers, json=payload, timeout=30)

        if response.status_code == 200:
            result = response.json()
            response_text = result['choices'][0]['message']['content']
            return response_text.encode("ascii", "ignore").decode('utf-8')
        elif response.status_code == 400:
            print(f"OpenAI API 400 Bad Request - Invalid payload or token issue")
            print(f"Response: {response.text}")
        else:
            print(
                f"OpenAI API error: {response.status_code} - {response.text}")
            return f"AI summary unavailable: API returned status {response.status_code}"
    except Exception as e:
        print(f"Error calling OpenAI API: {str(e)}")
        return f"AI summary unavailable: {str(e)}"


def _call_gemini(user_prompt: str) -> str:
    gemini_api_key = os.environ.get('QE_AGENT_API_KEY')

    if not gemini_api_key:
        return "SKIPPING AI summary unavailable: QE_AGENT_API_KEY not set"

    gemini_api_url = f"https://generativelanguage.googleapis.com/v1/models/gemini-2.0-flash:generateContent?key={gemini_api_key}"

    headers = {
        "Content-Type": "application/json"
    }

    payload = {"contents": [{"parts": [{"text": user_prompt}]}], "generationConfig": {
        "temperature": 0.5, "maxOutputTokens": 2000}}

    try:
        response = requests.post(
            gemini_api_url, headers=headers, json=payload, timeout=30)

        if response.status_code == 200:
            result = response.json()
            response_text = result['candidates'][0]['content']['parts'][0]['text']
            return response_text.encode("ascii", "ignore").decode('utf-8')
        elif response.status_code == 429:
            print(f"Gemini API 429 Too Many Requests - Rate limited")
            print(f"Response: {response.text}")
            return f"AI summary unavailable: API rate limited"
        else:
            print(
                f"Gemini API error: {response.status_code} - {response.text}")
            return f"AI summary unavailable: API returned status {response.status_code}"
    except Exception as e:
        print(f"Error calling Gemini API: {str(e)}")
        return f"AI summary unavailable: {str(e)}"


def generate_ai_summary(report, provider: str = "gemini") -> str:
    # Get provider from environment if set, otherwise use parameter default
    provider = os.environ.get('LLM_PROVIDER', 'gemini').lower()

    # Prepare test report summary for AI
    all_tests = report['tests']

    # Create test details for AI analysis
    test_details = []
    for test in all_tests:
        nodeid = test['nodeid'].replace('[', '-').replace(']', '')
        test_name = nodeid.split(
            '::')[-1].replace('test_', '', 1).replace('_', ' ').replace('validate', '').capitalize().strip()
        # Truncate error details to avoid payload size issues
        error_details = test.get('call', {}).get('longrepr', '') if test['outcome'] in [
            'failed', 'error'] else ''
        if error_details and len(error_details) > 1000:
            error_details = error_details[:1000] + "... (truncated)"

        test_info = {
            'test': test_name,
            'outcome': test['outcome'],
            'duration': round(test.get('duration', 0), 2),
            'keywords': test.get('keywords', []),
            'error_details': error_details,
            'more_info': next((prop.get('more_info') for prop in test.get('user_properties', []) if 'more_info' in prop), '')
        }
        test_details.append(test_info)

    user_prompt = f"""
You are generating a Slack-ready AI Agent Reliability Report.

Analyze the test results and produce a structured report.

ALL TEST DATA:
{json.dumps(test_details, indent=2)}

OUTPUT RULES (STRICT):

1. List ALL failed tests first, then passed tests. Do NOT skip any test.
2. Number sequentially (1, 2, 3...).
3. Format each test name in bold using Slack markdown:
   `Failed`: *<test name>*
   Passed: *<test name>*
4. Do NOT output JSON, dicts, arrays, or raw structures.
5. Convert structured data into readable lines.

FOR FAILED TESTS:
- Provide:
  - RCA: <Root cause under 120 words>
- RCA must explain likely system behavior (agent backlog, API schema issue, telemetry gap, timeout breach, etc.).
- Do NOT restate assertion language.
- If Case IDs exist, include up to 3 examples:
  -- Case ID: <id> - Resolution: <reason>

FOR PASSED TESTS:
- Only include:
  - More Info: <summary under 50 words>
- Include example Case IDs only if meaningful.

STYLE:
- Clear, technical, concise.
- No filler language.
- No emojis.
- No generic phrases like “test failed due to assertion error”.

Generate the numbered Slack report now.
"""

    # Call appropriate API based on provider
    if provider == 'openai':
        system_prompt = "You are an Experienced Quality Engineering generating concise test result summaries. Follow the exact format provided in the prompt."
        return _call_openai(system_prompt, user_prompt)
    elif provider == 'gemini':
        return _call_gemini(user_prompt)
    else:
        return f"AI summary unavailable: Unknown provider '{provider}'"


def send_slack_notification(report, test_run, test_env, test_run_id, slack_message_type="regular") -> None:
    third_party_config = load_third_party_config()
    test_type = report['tests'][0]['nodeid'].split('/')[0]
    build_url = f"{os.environ.get(third_party_config['scm_url_variable'])}/pipelines/results/{os.environ.get(third_party_config['scm_build_number_variable'])}" if os.environ.get(
        third_party_config['scm_url_variable']) else "Not Applicable"
    failed_tests_count_high = len([test for test in report['tests'] if (
        test['outcome'] == 'failed' or test['outcome'] == 'error') and 'high' in test.get('keywords', [])])
    failed_tests_count_med = len([test for test in report['tests'] if (
        test['outcome'] == 'failed' or test['outcome'] == 'error') and 'medium' in test.get('keywords', [])])
    failed_tests_count_low = len([test for test in report['tests'] if (
        test['outcome'] == 'failed' or test['outcome'] == 'error') and 'low' in test.get('keywords', [])])
    failed_tests_count_total = len(
        [test for test in report['tests'] if test['outcome'] == 'failed' or test['outcome'] == 'error'])
    skipped_tests_count = len(
        [test for test in report['tests'] if test['outcome'] == 'skipped'])
    eligible_test_count_total = len(report['tests']) - skipped_tests_count
    reliability_percentage = round(((eligible_test_count_total - failed_tests_count_total) /
                                   eligible_test_count_total) * 100, 2) if eligible_test_count_total > 0 else 0.0
    failed_skipped_report_url = f"{os.environ.get('jira_host_url')}/issues/?jql=project%20%3D%20{os.environ.get('jira_project_key')}%20AND%20%22trid%5BShort%20text%5D%22%20~%20%22{test_run_id}%22%20AND%20%22test%20status%5BDropdown%5D%22%20IN%20(Failed%2C%20Skipped)%20ORDER%20BY%20status%20ASC"
    failed_high_report_url = f"{os.environ.get('jira_host_url')}/issues/?jql=project%20%3D%20{os.environ.get('jira_project_key')}%20AND%20%22trid%5BShort%20text%5D%22%20~%20%22{test_run_id}%22%20AND%20%22test%20status%5BDropdown%5D%22%20IN%20(Failed)%20AND%20%22Test%20Tags%5BLabels%5D%22%20IN%20(high)%20ORDER%20BY%20status%20ASC"
    failed_medium_report_url = f"{os.environ.get('jira_host_url')}/issues/?jql=project%20%3D%20{os.environ.get('jira_project_key')}%20AND%20%22trid%5BShort%20text%5D%22%20~%20%22{test_run_id}%22%20AND%20%22test%20status%5BDropdown%5D%22%20IN%20(Failed)%20AND%20%22Test%20Tags%5BLabels%5D%22%20IN%20(medium)%20ORDER%20BY%20status%20ASC"
    failed_low_report_url = f"{os.environ.get('jira_host_url')}/issues/?jql=project%20%3D%20{os.environ.get('jira_project_key')}%20AND%20%22trid%5BShort%20text%5D%22%20~%20%22{test_run_id}%22%20AND%20%22test%20status%5BDropdown%5D%22%20IN%20(Failed)%20AND%20%22Test%20Tags%5BLabels%5D%22%20IN%20(low)%20ORDER%20BY%20status%20ASC"

    # print(
    #     f"📊 *Split:* 🔴 High: <{failed_high_report_url}|{failed_tests_count_high}> 🟡 Medium: <{failed_medium_report_url}|{failed_tests_count_med}> 🟢 Low: <{failed_low_report_url}|{failed_tests_count_low}> ⚫ Skipped: {skipped_tests_count}\n")

    # Generate AI summary if descriptive mode is enabled
    if slack_message_type == "detailed":
        ai_summary = generate_ai_summary(report)
        blocks = [
            {
                "type": "header",
                "text": {
                    "type": "plain_text",
                    "text": f"AI Agent Reliability Test Results - {test_env}",
                    "emoji": True
                }
            },
            {
                "type": "divider"
            },
            {
                "type": "section",
                "text": {
                    "type": "mrkdwn",
                    "text": f"{ai_summary}"
                }
            },
            {
                "type": "divider"
            },
            {
                "type": "section",
                "text": {
                    "type": "mrkdwn",
                    "text": f"- Current reliability is {reliability_percentage} (%)\n- Click to view <{failed_skipped_report_url}|Failed/Skipped> tests in Jira"
                }
            },
            {
                "type": "context",
                "elements": [
                    {
                        "type": "mrkdwn",
                        "text": f"Execution Date: _{datetime.fromtimestamp(report.get('created', 0)).strftime('%b-%d-%Y at %H:%M')}_\n_*Generated with Quality AIngineering - ΣβΔΨ*_"
                    }
                ]
            }
        ]
    else:
        blocks = [
            {
                "type": "header",
                "text": {
                    "type": "plain_text",
                    "text": f"{string.capwords(test_type.replace('_', ' '))} Results",
                    "emoji": True
                }
            },
            {
                "type": "divider"
            },
            {
                "type": "section",
                "text": {
                    "type": "mrkdwn",
                    "text": f"🚀 *Test Run:* {test_run}\n"
                        f"🌎 *Environment:* {test_env}\n"
                        f"❌ *Failed:* {report.get('summary', {}).get('failed', 0)}\n"
                        f"📊 *Split:* 🔴 High: <{failed_high_report_url}|{failed_tests_count_high}> 🟡 Medium: <{failed_medium_report_url}|{failed_tests_count_med}> 🟢 Low: <{failed_low_report_url}|{failed_tests_count_low}> ⚫ Skipped: {skipped_tests_count}\n"
                        f"📈 Click to open <{failed_skipped_report_url}|Failed/Skipped Tests> in Jira\n"
                }
            },
            {
                "type": "divider"
            },
            {
                "type": "section",
                "text": {
                    "type": "mrkdwn",
                    "text": f"🧪 *Total Tests:* {report.get('summary', {}).get('total', 0)}\n"
                        f"✅ *Passed:* {report.get('summary', {}).get('passed', 0)}\n"
                        f"🔄 *Executed:* {report.get('summary', {}).get('collected', 0)}\n"
                        f"⏸️ *Skipped:* {report.get('summary', {}).get('deselected', 0)}\n"
                        f"🛠️ Click to open <{build_url}|Build {os.environ.get('BITBUCKET_BUILD_NUMBER', '-')}>\n"
                }
            },
            {
                "type": "context",
                "elements": [
                    {
                        "type": "mrkdwn",
                        "text": f"Execution Date: _{datetime.fromtimestamp(report.get('created', 0)).strftime('%b-%d-%Y')}_"
                    }
                ]
            }
        ]

    slack_webhook_url = os.environ.get('slack_test_webhook') if test_run != 'Daily Run' else os.environ.get(
        'slack_dev_channel_webhook') if test_env.lower() == 'dev' else os.environ.get('slack_prod_channel_webhook')
    send_slack_message(slack_webhook_url=slack_webhook_url,
                       message_blocks=blocks)
