import csv
import json
from typing import List, Any
from models.vulnerability_intelligence import VulnerabilityIntelligence
from jinja2 import Template
from datetime import datetime

class VulnerabilityIntelligenceReportService:
    @staticmethod
    def generate_csv_report(vulnerabilities: List[VulnerabilityIntelligence], filename: str) -> None:
        with open(filename, mode='w', newline='', encoding='utf-8') as file:
            writer = csv.writer(file)

            writer.writerow([
                "ID", "Dates", "URLs", "Descriptions", "Reference URLs", "Base Scores", 
                "Severities", "Vulnerable Components", "Tags", "Weaknesses"
            ])

            for vuln in vulnerabilities:
                writer.writerow([
                    vuln.id,
                    VulnerabilityIntelligenceReportService._format_dates(vuln.dates),
                    VulnerabilityIntelligenceReportService._format_urls(vuln.urls),
                    VulnerabilityIntelligenceReportService._format_descriptions(vuln.descriptions),
                    "; ".join(vuln.reference_urls),
                    VulnerabilityIntelligenceReportService._format_base_scores(vuln.base_scores),
                    VulnerabilityIntelligenceReportService._format_severities(vuln.severities),
                    "; ".join(list(vuln.vulnerable_components)),
                    ", ".join(list(vuln.tags)),
                    ", ".join(vuln.weaknesses),
                ])
                
        print(f"Report was saved to {filename}")

    @staticmethod
    def generate_json_report(vulnerabilities: List[VulnerabilityIntelligence], filename: str) -> None:
        json_data = [VulnerabilityIntelligenceReportService._serialize_vulnerability(vuln) for vuln in vulnerabilities]
        json_data = VulnerabilityIntelligenceReportService._convert_sets_to_lists(json_data)
        with open(filename, 'w', encoding='utf-8') as file:
            json.dump(json_data, file, ensure_ascii=False, indent=4)
            
        print(f"Report was saved to {filename}")

    @staticmethod
    def _serialize_vulnerability(vuln: VulnerabilityIntelligence) -> dict:
        reference_urls = [*vuln.reference_urls]
        reference_urls.sort()
        return {
            "id": vuln.id,
            "dates": [{"source": date["source"], "date": date["date"]} for date in vuln.dates],
            "urls": [{"source": url["source"], "date": url["date"], "url": url["url"]} for url in vuln.urls],
            "titles": [{"source": title["source"], "date": title["date"], "text": title["text"]} for title in vuln.titles],
            "descriptions": [{"source": desc["source"], "date": desc["date"], "text": desc["text"]} for desc in vuln.descriptions],
            "reference_urls": reference_urls,
            "base_scores": [{"source": score["source"], "date": score["date"], "score": score["score"]} for score in vuln.base_scores],
            "severities": [{"source": severity["source"], "date": severity["date"], "severity": severity["severity"]} for severity in vuln.severities],
            "vulnerable_components": list(vuln.vulnerable_components),
            "tags": list(vuln.tags),
            "weaknesses": vuln.weaknesses,
        }

    @staticmethod
    def generate_html_report(vulnerabilities: List[VulnerabilityIntelligence], keywords, filename: str) -> None:
        template = VulnerabilityIntelligenceReportService._load_template('views/vulnerability_report_template.html')
        
        vulnerabilities = [VulnerabilityIntelligenceReportService._serialize_vulnerability(vuln) for vuln in vulnerabilities]
        vulnerabilities.reverse()

        date = datetime.now().strftime("%d/%m/%Y")

        rendered_html = template.render(vulnerabilities=vulnerabilities, keywords=keywords, date=date)

        with open(filename, 'w', encoding='utf-8') as file:
            file.write(rendered_html)
            
        print(f"Report was saved to {filename}")

    @staticmethod
    def _load_template(template_file: str) -> Template:
        with open(template_file, 'r', encoding='utf-8') as file:
            template_content = file.read()
        return Template(template_content)

    @staticmethod
    def _format_dates(dates: List[dict]) -> str:
        return "; ".join([f"({date['source']}): {date['date']}" for date in dates])

    @staticmethod
    def _format_urls(urls: List[dict]) -> str:
        return "; ".join([f"({url['source']}/{url['date']}): {url['url']}" for url in urls])

    @staticmethod
    def _format_descriptions(descriptions: List[dict]) -> str:
        return "; ".join([f"({desc['source']}/{desc['date']}): {desc['text']}" for desc in descriptions])

    @staticmethod
    def _format_base_scores(base_scores: List[dict]) -> str:
        return "; ".join([f"({score['source']}/{score['date']}): {score['score']}" for score in base_scores])

    @staticmethod
    def _format_severities(severities: List[dict]) -> str:
        return "; ".join([f"({severity['source']}/{severity['date']}): {severity['severity']}" for severity in severities])

    @staticmethod
    def _convert_sets_to_lists(data: Any) -> Any:
        if isinstance(data, dict):
            return {key: VulnerabilityIntelligenceReportService._convert_sets_to_lists(value) for key, value in data.items()}
        elif isinstance(data, list):
            return [VulnerabilityIntelligenceReportService._convert_sets_to_lists(item) for item in data]
        elif isinstance(data, set):
            return list(data)
        else:
            return data
