import httpx
import time
from typing import List
from dateutil import parser as dateutil_parser

from models.vulnerability import Vulnerability
from services.api.source import Source
from services.vulnerabilities.factories.vulnerability_factory import VulnerabilityFactory, DEFAULT_VALUES

class ExploitDBAPI(Source):
    def __init__(self, config):
        self.url = "https://www.exploit-db.com/"
        self.config = config 
        
    def search(self, keywords: List[str], max_results: int) -> List[Vulnerability]:
        vulnerabilities = []

        if max_results > 1000:
            max_results = 1000

        with httpx.Client() as client:
            initial_response = client.get(self.url)
            if initial_response.status_code != 200:
                return vulnerabilities

            headers = {
                "X-Requested-With": "XMLHttpRequest",
                "Referer": self.url,
                "Accept": "application/json, text/javascript, */*; q=0.01",
                "User-Agent": (
                    "Mozilla/5.0 (compatible; ExploitDBAPI/1.0; +https://example.com/)"
                ),
            }

            params = {
                "draw": 1,
                "columns[0][data]": "date_published",
                "columns[0][name]": "date_published",
                "columns[0][searchable]": "true",
                "columns[0][orderable]": "true",
                "columns[0][search][value]": "",
                "columns[0][search][regex]": "false",
                "columns[1][data]": "download",
                "columns[1][name]": "download",
                "columns[1][searchable]": "false",
                "columns[1][orderable]": "false",
                "columns[1][search][value]": "",
                "columns[1][search][regex]": "false",
                "columns[2][data]": "application_md5",
                "columns[2][name]": "application_md5",
                "columns[2][searchable]": "true",
                "columns[2][orderable]": "false",
                "columns[2][search][value]": "",
                "columns[2][search][regex]": "false",
                "columns[3][data]": "verified",
                "columns[3][name]": "verified",
                "columns[3][searchable]": "true",
                "columns[3][orderable]": "false",
                "columns[3][search][value]": "",
                "columns[3][search][regex]": "false",
                "columns[4][data]": "description",
                "columns[4][name]": "description",
                "columns[4][searchable]": "true",
                "columns[4][orderable]": "false",
                "columns[4][search][value]": "",
                "columns[4][search][regex]": "false",
                "columns[5][data]": "type_id",
                "columns[5][name]": "type_id",
                "columns[5][searchable]": "true",
                "columns[5][orderable]": "false",
                "columns[5][search][value]": "",
                "columns[5][search][regex]": "false",
                "columns[6][data]": "platform_id",
                "columns[6][name]": "platform_id",
                "columns[6][searchable]": "true",
                "columns[6][orderable]": "false",
                "columns[6][search][value]": "",
                "columns[6][search][regex]": "false",
                "columns[7][data]": "author_id",
                "columns[7][name]": "author_id",
                "columns[7][searchable]": "false",
                "columns[7][orderable]": "false",
                "columns[7][search][value]": "",
                "columns[7][search][regex]": "false",
                "columns[8][data]": "code",
                "columns[8][name]": "code.code",
                "columns[8][searchable]": "true",
                "columns[8][orderable]": "true",
                "columns[8][search][value]": "",
                "columns[8][search][regex]": "false",
                "columns[9][data]": "id",
                "columns[9][name]": "id",
                "columns[9][searchable]": "false",
                "columns[9][orderable]": "true",
                "columns[9][search][value]": "",
                "columns[9][search][regex]": "false",
                "order[0][column]": "9",
                "order[0][dir]": "desc",
                "start": 0,
                "length": max_results,
                "search[value]": " ".join(keywords),
                "search[regex]": "false",
                "author": "",
                "port": "",
                "type": "",
                "tag": "",
                "platform": "",
                "_": int(time.time() * 1000),
            }

            data_response = client.get(
                self.url + "?", params=params, headers=headers
            )

            if data_response.status_code != 200:
                return vulnerabilities

            data = data_response.json()

            for exploit in data.get("data", []):
                exploit_db_id = exploit.get("id", DEFAULT_VALUES["id"])

                description_list = exploit.get("description", [])
                title = (
                    description_list[1]
                    if len(description_list) > 1
                    else DEFAULT_VALUES["title"]
                )
                description = title

                date_published = exploit.get("date_published", DEFAULT_VALUES["date"])
                try:
                    parsed_date = dateutil_parser.parse(date_published)
                    date = parsed_date.strftime("%Y-%m-%d")
                except ValueError:
                    date = DEFAULT_VALUES["date"]

                url = f"https://www.exploit-db.com/exploits/{exploit_db_id}"

                platform_data = exploit.get("platform", {})
                platform_name = platform_data.get("platform", "").strip()

                type_data = exploit.get("type", {})
                type_name = type_data.get("display", "").strip()

                author_data = exploit.get("author", {})
                author_name = author_data.get("name", "").strip()

                application_path = exploit.get("application_path", "").strip()

                vulnerable_components = []
                if platform_name:
                    vulnerable_components.append(platform_name)
                if type_name:
                    vulnerable_components.append(type_name)
                if application_path:
                    vulnerable_components.append(application_path)

                tags = []
                codes = exploit.get("code", [])
                for code_entry in codes:
                    code_value = code_entry.get("code", "")
                    if code_value:
                        tags.append(code_value)

                if author_name:
                    tags.append(author_name)
                port = exploit.get("port", None)
                if port and port != 0:
                    tags.append(f"port:{port}")
                verified = exploit.get("verified", 0)
                if verified == 1:
                    tags.append("verified")

                exploit_tags = exploit.get("tags", [])
                for tag in exploit_tags:
                    tag_title = tag.get("title", "").strip()
                    if tag_title:
                        tags.append(tag_title)

                cve_codes = [
                    code_entry.get("code", exploit_db_id)
                    for code_entry in codes
                    if code_entry.get("code_type", "").lower() == "cve"
                ]
                
                id = f"CVE-{cve_codes[0]}" if cve_codes else exploit_db_id

                vulnerabilities.append(
                    VulnerabilityFactory.make(
                        id=id,
                        url=url,
                        source=self.__class__.__name__,
                        date=date,
                        base_score=DEFAULT_VALUES["base_score"],
                        base_severity=DEFAULT_VALUES["base_severity"],
                        description=description,
                        vulnerable_components=vulnerable_components,
                        tags=tags,
                    )
                )

        return vulnerabilities
