import os
import json
from datetime import datetime
import pandas as pd
from jinja2 import Template
import time
import openpyxl
from integrations.azure_blob import BlobStorageManager
import uuid
from dotenv import load_dotenv

load_dotenv()
SHOW_HCLTECH_TESTCASE_ID = str(os.getenv("HCLTech_TestCaseId", "true")).strip().lower() == "true"
REPORT_TITLE = os.getenv("REPORT_TITLE")

class TestReporter:
    def __init__(self):
        now = datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
        self.report_folder = "reports/screenshots"
        os.makedirs(self.report_folder, exist_ok=True)
        self.log = []
        self.report_file = f"reports/report_{now}.json"
        self.report_file_latest = "reports/latest_report.json"
        self.test_run_id = str(uuid.uuid4())
        self.blob_storage = BlobStorageManager()
        self.screenshot_urls = {}

    def capture_screenshot(self, driver, step=0, max_retries=3, initial_delay=1):
        """
        Capture screenshot with retry logic and exponential backoff.
        
        Args:
            driver: WebDriver instance
            step: Current test step number
            max_retries: Maximum number of retry attempts
            initial_delay: Initial delay between retries in seconds
        """
        path = os.path.join(self.report_folder, f"screen_{step}.png")
        delay = initial_delay
        
        for attempt in range(max_retries):
            try:
                driver.save_screenshot(path)
                
                # After saving the screenshot locally, upload to blob storage
                blob_url = self.blob_storage.upload_screenshot(path, self.test_run_id)
                self.screenshot_urls[step] = blob_url
                return path
                
            except Exception as e:
                if attempt == max_retries - 1:
                    print(f"[Warning] Failed to capture screenshot after {max_retries} attempts: {str(e)}")
                    return None
                    
                print(f"[Info] Screenshot attempt {attempt + 1} failed: {str(e)}")
                print(f"[Info] Retrying in {delay} seconds...")
                
                time.sleep(delay)
                delay *= 2
                
                try:
                    driver.current_activity
                except:
                    print("[Warning] Driver connection appears to be lost")

    def log_step(self, step, action, result, screenshot_path, bugs=None):
        """
        Log a test step with screenshot information and detailed status.
        """
        # Skip logging for "tested more than 3 times" cases
        if isinstance(action, dict) and "tested more than 3 times" in action.get("description", ""):
            return

        # Ensure result is properly categorized
        if not isinstance(result, str):
            result = str(result)
            
        result_lower = result.lower()
        if result_lower not in ["passed", "failed"]:
            if any(success in result_lower for success in ["success", "ok", "complete", "done", "✅"]):
                result = "passed"
            else:
                result = "failed"
                
        # Add more context to the action description if it's a failure
        if result == "failed" and isinstance(action, dict):
            if "description" not in action:
                action["description"] = "Action failed"
            elif not action["description"].startswith("Step failed:") and not action["description"].startswith("Action failed:"):
                action["description"] = f"Action failed: {action['description']}"

        step_data = {
            "step": step,
            "timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
            "action": action,
            "result": result,
            "screenshot_url": self.screenshot_urls.get(step)
        }
        self.log.append(step_data)

    def cleanup_reports(self):
        if os.path.exists(self.report_folder):
            for file in os.listdir(self.report_folder):
                os.remove(os.path.join(self.report_folder, file))
        
        for file in os.listdir("reports"):
            file_path = os.path.join("reports", file)
            if os.path.isfile(file_path):
                os.remove(file_path)

    def save_report_as_excel(self):
        rows = []
        for entry in self.log:
            action_data = entry.get("action", {})
            if isinstance(action_data, dict):
                action_type = action_data.get("action", "")
                locator = action_data.get("locator", "")
                value = action_data.get("value", "")
                description = action_data.get("description", "")
            else:
                action_type = locator = value = description = ""

            rows.append({
                "Step": entry["step"],
                "Action": action_type,
                "Locator": locator,
                "Value": value,
                "Description": description,
                "Result": entry["result"],
                "Screenshot": entry.get("screenshot_url", "")
            })

        df = pd.DataFrame(rows)
        excel_path = "reports/latest_report.xlsx"
        df.to_excel(excel_path, index=False)
        print(f"[OK] Excel Report saved: {excel_path}")

    def save_report(self):
        os.makedirs("reports", exist_ok=True)
        with open(self.report_file, "w", encoding="utf-8") as f:
            json.dump(self.log, f, indent=2)
        with open(self.report_file_latest, "w", encoding="utf-8") as f:
            json.dump(self.log, f, indent=2)
        self.save_report_as_excel()
        print(f"[OK] JSON Report saved: {self.report_file}")

    def cleanup_before_test(self):
        self.cleanup_reports()

    def get_exploration_summary(self, agent):
        """Generate a summary of feature exploration"""
        exploration_status = agent.get_exploration_status()
        return {
            "explored_features": exploration_status["explored_features"],
            "feature_weights": exploration_status["feature_weights"],
            "total_elements_tested": len(exploration_status["tested_elements"]),
            "exploration_progress": len(exploration_status["explored_features"]) / len(exploration_status["feature_weights"]) * 100
        }

def save_consolidated_excel_report(all_testcase_results, excel_path):
    import pandas as pd
    # all_testcase_results: list of dicts with keys: title, status, last_screenshot
    # Ensure parent directory exists
    os.makedirs(os.path.dirname(excel_path) if os.path.dirname(excel_path) else ".", exist_ok=True)
    df = pd.DataFrame(all_testcase_results)
    df.to_excel(excel_path, index=False)
    print(f"[OK] Consolidated Excel Report saved: {excel_path}")

# (Keep TestReporter.save_report_as_excel for legacy/single-case use, but only call the consolidated version from main.py)

class ConsolidatedHTMLReporter:
    def __init__(self, all_testcase_results, html_report=None, test_start_time=None, test_end_time=None, show_ado_id=True):
        # all_testcase_results: list of dicts with keys: title, status, steps, last_screenshot, ado_id (optional)
        # show_ado_id: whether to prepend [ado_id] to the title (default True for internal reports)
        self.test_start_time = test_start_time
        self.test_end_time = test_end_time
        self.all_testcase_results = all_testcase_results
        self.show_ado_id = show_ado_id
        if html_report is None:
            now = datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
            html_report = f"reports/consolidated_report_{now}.html"
        self.html_report = html_report
        template_path = os.path.join(os.path.dirname(__file__), "report_template.html")
        with open(template_path, "r", encoding="utf-8") as f:
            self.template = Template(f.read())

    def generate_html_report(self):
        # Prepare episodes for the template
        episodes = []
        total_steps = 0
        passed_steps = 0
        failed_steps = 0
        for tc in self.all_testcase_results:
            # Build steps list with pass/fail for each natural step
            steps = []
            for s in tc.get("steps", []):
                # s is a step log dict from NaturalLanguageTestRunner.log_step
                # Primary action text is the natural language description
                action_text = s.get("description", "")
                if not action_text:
                    # Fallback to action description when present and well-formed
                    action_obj = s.get("action_details", {}).get("action")
                    if isinstance(action_obj, dict):
                        action_text = action_obj.get("description", "")
                    elif isinstance(action_obj, str):
                        action_text = action_obj
                steps.append({
                    "action": action_text or "(no description)",
                    "result": s.get("result", "failed"),
                    "is_passed": s.get("result") == "passed",
                    "action_image": s.get("screenshot", ""),
                    "action_details": s.get("action_details", {})
                })
            # Fallback: if no granular steps captured, show single summary item
            if not steps:
                steps.append({
                    "action": tc["title"],
                    "result": tc["status"],
                    "is_passed": tc["status"] == "passed",
                    "action_image": tc.get("last_screenshot", ""),
                    "action_details": {}
                })
            display_title = tc["title"]
            try:
                # Include HCLTech ADO ID if show_ado_id is True and ado_id is present
                if self.show_ado_id and tc.get("ado_id"):
                    ado_id = tc.get("ado_id")
                    display_title = f"[{ado_id}] {display_title}"
            except Exception:
                pass
            episodes.append({
                "title": display_title,
                "status": tc["status"],
                "steps": steps,
            })
            # Stats at test-case level
            total_steps += 1
            if tc["status"] == "passed":
                passed_steps += 1
            else:
                failed_steps += 1

        # Use actual test times if provided, otherwise fall back to current time
        start_time = self.test_start_time.strftime("%Y-%m-%d %H:%M:%S") if self.test_start_time else datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        end_time = self.test_end_time.strftime("%Y-%m-%d %H:%M:%S") if self.test_end_time else datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        
        report_data = {
            "report_title": REPORT_TITLE,
            "run_start_time": start_time,
            "run_end_time": end_time,
            "total_steps": total_steps,
            "passed_steps": passed_steps,
            "failed_steps": failed_steps,
            "episodes": episodes
        }
        html_content = self.template.render(report=report_data)
        with open(self.html_report, "w", encoding="utf-8") as f:
            f.write(html_content)
        import logging
        logger = logging.getLogger(__name__)
        logger.info(f"Consolidated HTML Report generated: {self.html_report}")
        return {
        "total_steps": total_steps,
        "passed_steps": passed_steps,
        "failed_steps": failed_steps
    }
        
    def get_execution_stats(self):

        total_steps = 0
        passed_steps = 0
        failed_steps = 0

        for tc in self.all_testcase_results:
            steps = tc.get("steps", [])

            # If no granular steps, treat the test case itself as one step
            if not steps:
                total_steps += 1
                if tc["status"] == "passed":
                    passed_steps += 1
                else:
                    failed_steps += 1
            else:
                for s in steps:
                    total_steps += 1
                    if s.get("result") == "passed":
                        passed_steps += 1
                    else:
                        failed_steps += 1

        return {
            "total_steps": total_steps,
            "passed_steps": passed_steps,
            "failed_steps": failed_steps
        }
    
            
