"""Reporting system - Research memos, exports, reproducibility."""

import json
import os
from datetime import datetime, date
from pathlib import Path
from typing import Any, Dict, List, Optional, Union

import pandas as pd
from pydantic import BaseModel, Field


# ============================================================================
# DATA MODELS
# ============================================================================

class ResearchMemo(BaseModel):
    """Structured research memo."""
    
    id: str = Field(default_factory=lambda: datetime.now().strftime("%Y%m%d_%H%M%S"))
    title: str
    thesis: str
    key_findings: List[str]
    data_sources: List[str]
    methodology: str
    results: Dict[str, Any]
    conclusions: List[str]
    risks_and_limitations: List[str]
    recommendations: List[str]
    appendix: Optional[Dict[str, Any]] = None
    created_at: datetime = Field(default_factory=datetime.now)
    author: str = "Sigma"
    tags: List[str] = Field(default_factory=list)


class ExportConfig(BaseModel):
    """Configuration for exports."""
    
    format: str = "markdown"  # markdown, html, pdf, json
    include_charts: bool = True
    include_data: bool = True
    include_code: bool = False
    chart_format: str = "png"  # png, svg, html
    data_format: str = "csv"  # csv, excel, parquet


class ReproducibilityConfig(BaseModel):
    """Configuration for reproducibility."""
    
    query: str
    timestamp: datetime = Field(default_factory=datetime.now)
    data_hash: Optional[str] = None
    parameters: Dict[str, Any] = Field(default_factory=dict)
    data_sources: List[str] = Field(default_factory=list)
    random_seed: Optional[int] = None
    versions: Dict[str, str] = Field(default_factory=dict)


# ============================================================================
# MEMO GENERATOR
# ============================================================================

class MemoGenerator:
    """Generate research memos from analysis results."""
    
    MEMO_TEMPLATES = {
        "stock_analysis": """
# {title}

**Date:** {date}
**Author:** {author}

## Executive Summary

{thesis}

## Key Findings

{key_findings}

## Methodology

{methodology}

## Results

{results}

## Conclusions

{conclusions}

## Risks and Limitations

{risks}

## Recommendations

{recommendations}

---
*Generated by Sigma Financial Intelligence*
""",
        "comparison": """
# {title}

**Date:** {date}
**Author:** {author}

## Overview

{thesis}

## Comparison Summary

{key_findings}

## Detailed Analysis

{results}

## Winner Analysis

{conclusions}

## Considerations

{risks}

## Final Recommendation

{recommendations}

---
*Generated by Sigma Financial Intelligence*
""",
        "backtest": """
# {title}

**Date:** {date}
**Author:** {author}

## Strategy Overview

{thesis}

## Performance Summary

{key_findings}

## Methodology

{methodology}

## Detailed Results

{results}

## Risk Analysis

{risks}

## Conclusions and Next Steps

{conclusions}

{recommendations}

---
*Generated by Sigma Financial Intelligence*
""",
    }
    
    def generate_memo(
        self,
        analysis_type: str,
        title: str,
        thesis: str,
        key_findings: List[str],
        methodology: str,
        results: Dict[str, Any],
        conclusions: List[str],
        risks: List[str],
        recommendations: List[str],
        author: str = "Sigma",
    ) -> ResearchMemo:
        """Generate a structured research memo."""
        
        return ResearchMemo(
            title=title,
            thesis=thesis,
            key_findings=key_findings,
            data_sources=[],  # To be filled
            methodology=methodology,
            results=results,
            conclusions=conclusions,
            risks_and_limitations=risks,
            recommendations=recommendations,
            author=author,
            tags=[analysis_type],
        )
    
    def memo_to_markdown(self, memo: ResearchMemo, template: str = "stock_analysis") -> str:
        """Convert memo to markdown format."""
        
        template_str = self.MEMO_TEMPLATES.get(template, self.MEMO_TEMPLATES["stock_analysis"])
        
        # Format lists
        key_findings_str = "\n".join([f"- {f}" for f in memo.key_findings])
        conclusions_str = "\n".join([f"- {c}" for c in memo.conclusions])
        risks_str = "\n".join([f"- {r}" for r in memo.risks_and_limitations])
        recommendations_str = "\n".join([f"- {r}" for r in memo.recommendations])
        
        # Format results
        results_str = self._format_results(memo.results)
        
        return template_str.format(
            title=memo.title,
            date=memo.created_at.strftime("%Y-%m-%d"),
            author=memo.author,
            thesis=memo.thesis,
            key_findings=key_findings_str,
            methodology=memo.methodology,
            results=results_str,
            conclusions=conclusions_str,
            risks=risks_str,
            recommendations=recommendations_str,
        )
    
    def _format_results(self, results: Dict[str, Any], indent: int = 0) -> str:
        """Format results dictionary as markdown."""
        
        lines = []
        prefix = "  " * indent
        
        for key, value in results.items():
            if isinstance(value, dict):
                lines.append(f"{prefix}### {key.replace('_', ' ').title()}")
                lines.append(self._format_results(value, indent + 1))
            elif isinstance(value, list):
                lines.append(f"{prefix}**{key.replace('_', ' ').title()}:**")
                for item in value:
                    lines.append(f"{prefix}- {item}")
            elif isinstance(value, (int, float)):
                if isinstance(value, float):
                    if abs(value) < 1:
                        formatted = f"{value:.2%}"
                    else:
                        formatted = f"{value:,.2f}"
                else:
                    formatted = f"{value:,}"
                lines.append(f"{prefix}**{key.replace('_', ' ').title()}:** {formatted}")
            else:
                lines.append(f"{prefix}**{key.replace('_', ' ').title()}:** {value}")
        
        return "\n".join(lines)
    
    def quick_summary(
        self,
        ticker: str,
        analysis_results: Dict[str, Any],
    ) -> str:
        """Generate a quick 1-paragraph summary."""
        
        # Extract key metrics
        metrics = analysis_results.get("metrics", {})
        
        total_return = metrics.get("total_return", 0)
        sharpe = metrics.get("sharpe_ratio", 0)
        max_dd = metrics.get("max_drawdown", 0)
        
        # Generate summary
        performance = "strong" if total_return > 0.15 else "moderate" if total_return > 0 else "weak"
        risk_adj = "excellent" if sharpe > 1.5 else "good" if sharpe > 1 else "acceptable" if sharpe > 0.5 else "poor"
        
        summary = (
            f"{ticker} has shown {performance} performance with a total return of {total_return:.1%}. "
            f"Risk-adjusted returns are {risk_adj} (Sharpe: {sharpe:.2f}). "
            f"Maximum drawdown was {abs(max_dd):.1%}."
        )
        
        return summary


# ============================================================================
# EXPORT ENGINE
# ============================================================================

class ExportEngine:
    """Export analysis results in various formats."""
    
    def __init__(self, output_dir: str = None):
        self.output_dir = Path(output_dir or os.path.expanduser("~/Documents/Sigma"))
        self.output_dir.mkdir(parents=True, exist_ok=True)
    
    def export_memo(
        self,
        memo: ResearchMemo,
        config: ExportConfig = None,
    ) -> str:
        """Export a research memo."""
        
        config = config or ExportConfig()
        
        # Generate filename
        filename = f"{memo.id}_{memo.title.replace(' ', '_')[:30]}"
        
        if config.format == "markdown":
            return self._export_markdown(memo, filename)
        elif config.format == "html":
            return self._export_html(memo, filename)
        elif config.format == "json":
            return self._export_json(memo, filename)
        else:
            return self._export_markdown(memo, filename)
    
    def _export_markdown(self, memo: ResearchMemo, filename: str) -> str:
        """Export as markdown."""
        
        generator = MemoGenerator()
        content = generator.memo_to_markdown(memo)
        
        filepath = self.output_dir / f"{filename}.md"
        filepath.write_text(content)
        
        return str(filepath)
    
    def _export_html(self, memo: ResearchMemo, filename: str) -> str:
        """Export as HTML."""
        
        generator = MemoGenerator()
        markdown_content = generator.memo_to_markdown(memo)
        
        # Simple markdown to HTML conversion
        html_content = self._markdown_to_html(markdown_content)
        
        # Wrap in HTML template
        html = f"""
<!DOCTYPE html>
<html>
<head>
    <title>{memo.title}</title>
    <style>
        body {{
            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
            max-width: 800px;
            margin: 40px auto;
            padding: 20px;
            line-height: 1.6;
            color: #333;
        }}
        h1 {{ color: #1a1a1a; border-bottom: 2px solid #0066cc; padding-bottom: 10px; }}
        h2 {{ color: #333; margin-top: 30px; }}
        h3 {{ color: #555; }}
        ul {{ padding-left: 20px; }}
        li {{ margin: 5px 0; }}
        strong {{ color: #1a1a1a; }}
        .meta {{ color: #666; font-size: 0.9em; margin-bottom: 20px; }}
    </style>
</head>
<body>
{html_content}
</body>
</html>
"""
        
        filepath = self.output_dir / f"{filename}.html"
        filepath.write_text(html)
        
        return str(filepath)
    
    def _export_json(self, memo: ResearchMemo, filename: str) -> str:
        """Export as JSON."""
        
        filepath = self.output_dir / f"{filename}.json"
        filepath.write_text(memo.model_dump_json(indent=2))
        
        return str(filepath)
    
    def _markdown_to_html(self, markdown: str) -> str:
        """Simple markdown to HTML conversion."""
        
        lines = markdown.split("\n")
        html_lines = []
        in_list = False
        
        for line in lines:
            # Headers
            if line.startswith("# "):
                html_lines.append(f"<h1>{line[2:]}</h1>")
            elif line.startswith("## "):
                html_lines.append(f"<h2>{line[3:]}</h2>")
            elif line.startswith("### "):
                html_lines.append(f"<h3>{line[4:]}</h3>")
            # Lists
            elif line.startswith("- "):
                if not in_list:
                    html_lines.append("<ul>")
                    in_list = True
                html_lines.append(f"<li>{line[2:]}</li>")
            # Bold
            elif line.startswith("**") and ":**" in line:
                if in_list:
                    html_lines.append("</ul>")
                    in_list = False
                parts = line.split(":**", 1)
                key = parts[0].replace("**", "")
                value = parts[1] if len(parts) > 1 else ""
                html_lines.append(f"<p><strong>{key}:</strong>{value}</p>")
            # Empty line
            elif not line.strip():
                if in_list:
                    html_lines.append("</ul>")
                    in_list = False
                html_lines.append("<br>")
            # Regular text
            else:
                if in_list:
                    html_lines.append("</ul>")
                    in_list = False
                html_lines.append(f"<p>{line}</p>")
        
        if in_list:
            html_lines.append("</ul>")
        
        return "\n".join(html_lines)
    
    def export_data(
        self,
        data: pd.DataFrame,
        filename: str,
        format: str = "csv",
    ) -> str:
        """Export DataFrame to file."""
        
        if format == "csv":
            filepath = self.output_dir / f"{filename}.csv"
            data.to_csv(filepath)
        elif format == "excel":
            filepath = self.output_dir / f"{filename}.xlsx"
            data.to_excel(filepath)
        elif format == "parquet":
            filepath = self.output_dir / f"{filename}.parquet"
            data.to_parquet(filepath)
        else:
            filepath = self.output_dir / f"{filename}.csv"
            data.to_csv(filepath)
        
        return str(filepath)
    
    def export_chart(
        self,
        fig,  # plotly figure
        filename: str,
        format: str = "png",
        scale: int = 2,
    ) -> str:
        """Export chart to file."""
        
        filepath = self.output_dir / f"{filename}.{format}"
        
        if format == "html":
            fig.write_html(str(filepath))
        else:
            fig.write_image(str(filepath), format=format, scale=scale)
        
        return str(filepath)


# ============================================================================
# REPRODUCIBILITY ENGINE
# ============================================================================

class ReproducibilityEngine:
    """Ensure reproducibility of analysis."""
    
    def __init__(self, config_dir: str = None):
        self.config_dir = Path(config_dir or os.path.expanduser("~/.sigma/reproducibility"))
        self.config_dir.mkdir(parents=True, exist_ok=True)
    
    def save_config(
        self,
        query: str,
        parameters: Dict[str, Any],
        data_sources: List[str],
        random_seed: Optional[int] = None,
    ) -> str:
        """Save reproducibility configuration."""
        
        import hashlib
        
        config = ReproducibilityConfig(
            query=query,
            parameters=parameters,
            data_sources=data_sources,
            random_seed=random_seed,
            versions=self._get_versions(),
        )
        
        # Generate config ID
        config_hash = hashlib.sha256(
            f"{query}{json.dumps(parameters, sort_keys=True)}".encode()
        ).hexdigest()[:12]
        
        config_id = f"{datetime.now().strftime('%Y%m%d_%H%M%S')}_{config_hash}"
        
        # Save config
        filepath = self.config_dir / f"{config_id}.json"
        filepath.write_text(config.model_dump_json(indent=2))
        
        return config_id
    
    def load_config(self, config_id: str) -> Optional[ReproducibilityConfig]:
        """Load reproducibility configuration."""
        
        filepath = self.config_dir / f"{config_id}.json"
        
        if not filepath.exists():
            return None
        
        data = json.loads(filepath.read_text())
        return ReproducibilityConfig(**data)
    
    def _get_versions(self) -> Dict[str, str]:
        """Get versions of key packages."""
        
        versions = {}
        
        packages = ["pandas", "numpy", "scipy", "plotly", "yfinance"]
        
        for package in packages:
            try:
                import importlib
                mod = importlib.import_module(package)
                versions[package] = getattr(mod, "__version__", "unknown")
            except ImportError:
                versions[package] = "not installed"
        
        return versions
    
    def generate_reproduction_script(
        self,
        config: ReproducibilityConfig,
    ) -> str:
        """Generate a Python script to reproduce the analysis."""
        
        script = f'''#!/usr/bin/env python3
"""
Reproduction Script
Generated: {datetime.now().isoformat()}
Original Query: {config.query}
"""

import pandas as pd
import numpy as np

# Set random seed for reproducibility
{f"np.random.seed({config.random_seed})" if config.random_seed else "# No random seed specified"}

# Parameters
PARAMETERS = {json.dumps(config.parameters, indent=4)}

# Data sources
DATA_SOURCES = {json.dumps(config.data_sources, indent=4)}

# Expected versions (for verification)
EXPECTED_VERSIONS = {json.dumps(config.versions, indent=4)}

def verify_versions():
    """Verify package versions match original analysis."""
    import warnings
    for package, expected in EXPECTED_VERSIONS.items():
        try:
            import importlib
            mod = importlib.import_module(package)
            actual = getattr(mod, "__version__", "unknown")
            if actual != expected:
                warnings.warn(f"{{package}} version mismatch: {{actual}} vs {{expected}}")
        except ImportError:
            warnings.warn(f"{{package}} not installed")

def main():
    """Reproduce the analysis."""
    verify_versions()
    
    # TODO: Add reproduction code
    print("Reproduction script generated. Add your analysis code here.")
    print(f"Original query: {config.query}")

if __name__ == "__main__":
    main()
'''
        
        return script


# ============================================================================
# SESSION LOGGER
# ============================================================================

class SessionLogger:
    """Log analysis sessions for audit and reproducibility."""
    
    def __init__(self, log_dir: str = None):
        self.log_dir = Path(log_dir or os.path.expanduser("~/.sigma/logs"))
        self.log_dir.mkdir(parents=True, exist_ok=True)
        self.session_id = datetime.now().strftime("%Y%m%d_%H%M%S")
        self.entries = []
    
    def log_query(self, query: str, response: str = None):
        """Log a query and response."""
        
        entry = {
            "timestamp": datetime.now().isoformat(),
            "type": "query",
            "query": query,
            "response": response,
        }
        self.entries.append(entry)
    
    def log_action(self, action: str, details: Dict[str, Any] = None):
        """Log an action."""
        
        entry = {
            "timestamp": datetime.now().isoformat(),
            "type": "action",
            "action": action,
            "details": details or {},
        }
        self.entries.append(entry)
    
    def log_error(self, error: str, context: Dict[str, Any] = None):
        """Log an error."""
        
        entry = {
            "timestamp": datetime.now().isoformat(),
            "type": "error",
            "error": error,
            "context": context or {},
        }
        self.entries.append(entry)
    
    def save_session(self) -> str:
        """Save session log to file."""
        
        filepath = self.log_dir / f"session_{self.session_id}.json"
        
        session_data = {
            "session_id": self.session_id,
            "start_time": self.entries[0]["timestamp"] if self.entries else None,
            "end_time": datetime.now().isoformat(),
            "entry_count": len(self.entries),
            "entries": self.entries,
        }
        
        filepath.write_text(json.dumps(session_data, indent=2))
        
        return str(filepath)
