use crate::models::{Finding, Severity};
use crate::output::OutputFormatter;
use anyhow::Result;
use std::fs;
use std::path::Path;

pub struct HtmlOutput;

impl HtmlOutput {
    pub fn new() -> Self {
        Self
    }

    fn create_html(&self, findings: &[Finding]) -> String {
        let mut html = String::new();

        // HTML header
        html.push_str(r#"<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Leaktor Security Scan Report</title>
    <meta name="description" content="Security scan report generated by Leaktor">
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }
        body {
            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif;
            background: #0f1419;
            color: #e6edf3;
            padding: 2rem;
            line-height: 1.6;
        }
        .container {
            max-width: 1200px;
            margin: 0 auto;
        }
        h1 {
            color: #58a6ff;
            margin-bottom: 0.5rem;
            font-size: 2.5rem;
        }
        .subtitle {
            color: #8b949e;
            margin-bottom: 2rem;
        }
        .summary {
            display: grid;
            grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
            gap: 1rem;
            margin-bottom: 2rem;
        }
        .stat-card {
            background: #161b22;
            border: 1px solid #30363d;
            border-radius: 6px;
            padding: 1.5rem;
        }
        .stat-value {
            font-size: 2rem;
            font-weight: bold;
            margin-bottom: 0.5rem;
        }
        .stat-label {
            color: #8b949e;
            font-size: 0.875rem;
        }
        .critical { color: #f85149; }
        .high { color: #ff7b72; }
        .medium { color: #d29922; }
        .low { color: #58a6ff; }
        .finding {
            background: #161b22;
            border: 1px solid #30363d;
            border-radius: 6px;
            margin-bottom: 1rem;
            overflow: hidden;
        }
        .finding-header {
            padding: 1rem 1.5rem;
            border-bottom: 1px solid #30363d;
            display: flex;
            justify-content: space-between;
            align-items: center;
        }
        .finding-title {
            font-weight: 600;
            font-size: 1.1rem;
        }
        .severity-badge {
            padding: 0.25rem 0.75rem;
            border-radius: 12px;
            font-size: 0.75rem;
            font-weight: 600;
            text-transform: uppercase;
        }
        .severity-critical {
            background: #f851491a;
            color: #f85149;
            border: 1px solid #f85149;
        }
        .severity-high {
            background: #ff7b721a;
            color: #ff7b72;
            border: 1px solid #ff7b72;
        }
        .severity-medium {
            background: #d299221a;
            color: #d29922;
            border: 1px solid #d29922;
        }
        .severity-low {
            background: #58a6ff1a;
            color: #58a6ff;
            border: 1px solid #58a6ff;
        }
        .finding-body {
            padding: 1.5rem;
        }
        .finding-meta {
            display: grid;
            grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
            gap: 1rem;
            margin-bottom: 1rem;
        }
        .meta-item {
            font-size: 0.875rem;
        }
        .meta-label {
            color: #8b949e;
            display: block;
            margin-bottom: 0.25rem;
        }
        .code-block {
            background: #0d1117;
            border: 1px solid #30363d;
            border-radius: 6px;
            padding: 1rem;
            overflow-x: auto;
            font-family: 'Consolas', 'Monaco', monospace;
            font-size: 0.875rem;
        }
        .code-line {
            margin: 0.25rem 0;
        }
        .code-highlight {
            background: #bb800926;
            padding: 0.125rem 0;
        }
        .validated {
            display: inline-block;
            padding: 0.25rem 0.5rem;
            background: #238636;
            color: #fff;
            border-radius: 4px;
            font-size: 0.75rem;
            margin-left: 0.5rem;
        }
        .not-validated {
            display: inline-block;
            padding: 0.25rem 0.5rem;
            background: #6e7681;
            color: #fff;
            border-radius: 4px;
            font-size: 0.75rem;
            margin-left: 0.5rem;
        }
        footer {
            margin-top: 3rem;
            padding-top: 2rem;
            border-top: 1px solid #30363d;
            text-align: center;
            color: #8b949e;
            font-size: 0.875rem;
        }
        .search-box {
            margin: 2rem 0;
            position: relative;
        }
        .search-box input {
            width: 100%;
            padding: 0.75rem 1rem;
            background: #161b22;
            border: 1px solid #30363d;
            border-radius: 6px;
            color: #e6edf3;
            font-size: 1rem;
        }
        .search-box input:focus {
            outline: none;
            border-color: #58a6ff;
        }
        .filter-buttons {
            display: flex;
            gap: 0.5rem;
            margin: 1rem 0;
            flex-wrap: wrap;
        }
        .filter-btn {
            padding: 0.5rem 1rem;
            background: #161b22;
            border: 1px solid #30363d;
            border-radius: 6px;
            color: #e6edf3;
            cursor: pointer;
            font-size: 0.875rem;
            transition: all 0.2s;
        }
        .filter-btn:hover {
            background: #21262d;
        }
        .filter-btn.active {
            background: #58a6ff;
            border-color: #58a6ff;
            color: #0d1117;
        }
        .no-results {
            text-align: center;
            padding: 3rem;
            color: #8b949e;
        }
        .copy-btn {
            display: inline-block;
            padding: 0.25rem 0.5rem;
            background: #21262d;
            border: 1px solid #30363d;
            border-radius: 4px;
            color: #58a6ff;
            cursor: pointer;
            font-size: 0.75rem;
            margin-left: 0.5rem;
            transition: all 0.2s;
        }
        .copy-btn:hover {
            background: #30363d;
        }
        .stats-chart {
            display: flex;
            height: 8px;
            border-radius: 4px;
            overflow: hidden;
            margin-top: 1rem;
        }
        .chart-segment {
            height: 100%;
        }
        .false-positive-badge {
            display: inline-block;
            padding: 0.25rem 0.5rem;
            background: #8b949e1a;
            color: #8b949e;
            border: 1px solid #8b949e;
            border-radius: 4px;
            font-size: 0.75rem;
            margin-left: 0.5rem;
        }
        @media print {
            body {
                background: white;
                color: black;
            }
            .search-box, .filter-buttons {
                display: none;
            }
        }
    </style>
</head>
<body>
    <div class="container">
        <h1>Leaktor Security Scan</h1>
        <p class="subtitle">Secrets Detection Report</p>
"#);

        // Summary statistics
        let (critical, high, medium, low) = self.count_by_severity(findings);
        let validated = findings
            .iter()
            .filter(|f| f.secret.validated == Some(true))
            .count();

        let _false_positives = findings
            .iter()
            .filter(|f| f.is_likely_false_positive())
            .count();

        html.push_str(&format!(
            r#"
        <div class="summary">
            <div class="stat-card">
                <div class="stat-value">{}</div>
                <div class="stat-label">Total Findings</div>
            </div>
            <div class="stat-card">
                <div class="stat-value critical">{}</div>
                <div class="stat-label">Critical</div>
            </div>
            <div class="stat-card">
                <div class="stat-value high">{}</div>
                <div class="stat-label">High</div>
            </div>
            <div class="stat-card">
                <div class="stat-value medium">{}</div>
                <div class="stat-label">Medium</div>
            </div>
            <div class="stat-card">
                <div class="stat-value low">{}</div>
                <div class="stat-label">Low</div>
            </div>
            <div class="stat-card">
                <div class="stat-value">{}</div>
                <div class="stat-label">Validated</div>
            </div>
        </div>
"#,
            findings.len(),
            critical,
            high,
            medium,
            low,
            validated
        ));

        // Add visualization chart
        if !findings.is_empty() {
            let total = findings.len() as f64;
            let crit_pct = (critical as f64 / total * 100.0) as u32;
            let high_pct = (high as f64 / total * 100.0) as u32;
            let med_pct = (medium as f64 / total * 100.0) as u32;
            let low_pct = (low as f64 / total * 100.0) as u32;

            html.push_str(&format!(
                r#"
        <div class="stats-chart">
            <div class="chart-segment" style="width: {}%; background: #f85149;"></div>
            <div class="chart-segment" style="width: {}%; background: #ff7b72;"></div>
            <div class="chart-segment" style="width: {}%; background: #d29922;"></div>
            <div class="chart-segment" style="width: {}%; background: #58a6ff;"></div>
        </div>
"#,
                crit_pct, high_pct, med_pct, low_pct
            ));
        }

        // Add search and filter UI
        html.push_str(r#"
        <div class="search-box">
            <input type="text" id="searchInput" placeholder="Search findings by file, type, or content...">
        </div>
        <div class="filter-buttons">
            <button class="filter-btn active" data-filter="all">All</button>
            <button class="filter-btn" data-filter="critical">Critical</button>
            <button class="filter-btn" data-filter="high">High</button>
            <button class="filter-btn" data-filter="medium">Medium</button>
            <button class="filter-btn" data-filter="low">Low</button>
            <button class="filter-btn" data-filter="validated">Validated</button>
            <button class="filter-btn" data-filter="false-positive">Likely False Positives</button>
        </div>
        <div id="noResults" class="no-results" style="display: none;">
            <p>No findings match your search criteria</p>
        </div>
"#);

        // Findings
        html.push_str("<h2 style='margin: 2rem 0 1rem 0; color: #e6edf3;'>Findings</h2>");

        for finding in findings {
            let severity_class = match finding.severity() {
                Severity::Critical => "critical",
                Severity::High => "high",
                Severity::Medium => "medium",
                Severity::Low => "low",
            };

            let validated_badge = match finding.secret.validated {
                Some(true) => "<span class='validated'>[OK] VALIDATED</span>",
                Some(false) => "<span class='not-validated'>[X] INVALID</span>",
                None => "",
            };

            let false_positive_badge = if finding.is_likely_false_positive() {
                "<span class='false-positive-badge'>[!] Likely False Positive</span>"
            } else {
                ""
            };

            let is_validated = if finding.secret.validated == Some(true) {
                "true"
            } else {
                "false"
            };
            let is_false_positive = if finding.is_likely_false_positive() {
                "true"
            } else {
                "false"
            };

            html.push_str(&format!(
                r#"
        <div class="finding" data-severity="{}" data-validated="{}" data-false-positive="{}" data-file="{}" data-type="{}">
            <div class="finding-header">
                <div class="finding-title">{}{}{}</div>
                <span class="severity-badge severity-{}">{}</span>
            </div>
            <div class="finding-body">
                <div class="finding-meta">
                    <div class="meta-item">
                        <span class="meta-label">File</span>
                        {}
                    </div>
                    <div class="meta-item">
                        <span class="meta-label">Location</span>
                        Line {}
                    </div>
                    <div class="meta-item">
                        <span class="meta-label">Confidence</span>
                        {:.0}%
                    </div>
                    <div class="meta-item">
                        <span class="meta-label">Entropy</span>
                        {:.2}
                    </div>
                </div>
                <div class="code-block">
"#,
                severity_class,
                is_validated,
                is_false_positive,
                html_escape(&finding.location.file_path.to_string_lossy()),
                html_escape(finding.secret.secret_type.as_str()),
                finding.secret.secret_type.as_str(),
                validated_badge,
                false_positive_badge,
                severity_class,
                finding.severity().as_str(),
                html_escape(&finding.location.file_path.to_string_lossy()),
                finding.location.line_number,
                finding.secret.confidence * 100.0,
                finding.secret.entropy
            ));

            // Code context
            if let Some(ref before) = finding.context.line_before {
                html.push_str(&format!(
                    "<div class='code-line'>{}</div>",
                    html_escape(before)
                ));
            }
            html.push_str(&format!(
                "<div class='code-line code-highlight'>{}</div>",
                html_escape(&finding.context.line_content)
            ));
            if let Some(ref after) = finding.context.line_after {
                html.push_str(&format!(
                    "<div class='code-line'>{}</div>",
                    html_escape(after)
                ));
            }

            html.push_str(
                r#"
                </div>
            </div>
        </div>
"#,
            );
        }

        // Footer and JavaScript
        html.push_str(&format!(
            r#"
        <footer>
            Generated by Leaktor v{} on {}<br>
            <a href="https://github.com/reschjonas/leaktor" style="color: #58a6ff;">github.com/reschjonas/leaktor</a>
        </footer>
    </div>
    <script>
        // Search functionality
        const searchInput = document.getElementById('searchInput');
        const findings = document.querySelectorAll('.finding');
        const noResults = document.getElementById('noResults');
        const filterButtons = document.querySelectorAll('.filter-btn');
        let currentFilter = 'all';

        searchInput.addEventListener('input', filterFindings);

        filterButtons.forEach(btn => {{
            btn.addEventListener('click', function() {{
                filterButtons.forEach(b => b.classList.remove('active'));
                this.classList.add('active');
                currentFilter = this.dataset.filter;
                filterFindings();
            }});
        }});

        function filterFindings() {{
            const searchTerm = searchInput.value.toLowerCase();
            let visibleCount = 0;

            findings.forEach(finding => {{
                const file = finding.dataset.file.toLowerCase();
                const type = finding.dataset.type.toLowerCase();
                const content = finding.textContent.toLowerCase();
                const severity = finding.dataset.severity;
                const validated = finding.dataset.validated === 'true';
                const falsePositive = finding.dataset.falsePositive === 'true';

                let matchesSearch = file.includes(searchTerm) ||
                                   type.includes(searchTerm) ||
                                   content.includes(searchTerm);

                let matchesFilter = true;
                if (currentFilter !== 'all') {{
                    if (currentFilter === 'validated') {{
                        matchesFilter = validated;
                    }} else if (currentFilter === 'false-positive') {{
                        matchesFilter = falsePositive;
                    }} else {{
                        matchesFilter = severity === currentFilter;
                    }}
                }}

                if (matchesSearch && matchesFilter) {{
                    finding.style.display = 'block';
                    visibleCount++;
                }} else {{
                    finding.style.display = 'none';
                }}
            }});

            noResults.style.display = visibleCount === 0 ? 'block' : 'none';
        }}

        // Initialize
        filterFindings();
    </script>
</body>
</html>
"#,
            env!("CARGO_PKG_VERSION"),
            chrono::Utc::now().format("%Y-%m-%d %H:%M:%S UTC")
        ));

        html
    }

    fn count_by_severity(&self, findings: &[Finding]) -> (usize, usize, usize, usize) {
        let mut critical = 0;
        let mut high = 0;
        let mut medium = 0;
        let mut low = 0;

        for finding in findings {
            match finding.severity() {
                Severity::Critical => critical += 1,
                Severity::High => high += 1,
                Severity::Medium => medium += 1,
                Severity::Low => low += 1,
            }
        }

        (critical, high, medium, low)
    }
}

fn html_escape(text: &str) -> String {
    text.replace('&', "&amp;")
        .replace('<', "&lt;")
        .replace('>', "&gt;")
        .replace('"', "&quot;")
        .replace('\'', "&#x27;")
}

impl OutputFormatter for HtmlOutput {
    fn format(&self, findings: &[Finding]) -> Result<String> {
        Ok(self.create_html(findings))
    }

    fn write_to_file(&self, findings: &[Finding], path: &Path) -> Result<()> {
        let html = self.format(findings)?;
        fs::write(path, html)?;
        Ok(())
    }
}

impl Default for HtmlOutput {
    fn default() -> Self {
        Self::new()
    }
}
