import ssl
import socket
import logging
from datetime import datetime, timezone
from .base import Finding

logger = logging.getLogger(__name__)

CHECK_NAME = 'tls_expiry'
TIMEOUT = 10


def _get_cert_info(domain: str) -> dict:
    ctx = ssl.create_default_context()
    try:
        with ctx.wrap_socket(socket.create_connection((domain, 443), timeout=TIMEOUT),
                             server_hostname=domain) as s:
            cert = s.getpeercert()
        return {'cert': cert, 'error': None}
    except ssl.SSLCertVerificationError as e:
        return {'cert': None, 'error': f'SSL verification failed: {e}'}
    except Exception as e:
        return {'cert': None, 'error': str(e)}


def _days_until_expiry(not_after: str) -> int:
    expiry = datetime.strptime(not_after, '%b %d %H:%M:%S %Y %Z').replace(tzinfo=timezone.utc)
    now = datetime.now(tz=timezone.utc)
    return (expiry - now).days


def _cert_covers_domain(cert: dict, domain: str) -> bool:
    san_list = [v for t, v in cert.get('subjectAltName', []) if t == 'DNS']
    for san in san_list:
        if san == domain:
            return True
        if san.startswith('*.') and domain.endswith(san[1:]):
            return True
    if not san_list:
        cn = dict(x[0] for x in cert.get('subject', [])).get('commonName', '')
        return cn == domain
    return False


def run(profile) -> list:
    findings = []
    domains = profile.domains or []

    if not domains:
        return [Finding(
            check_name=CHECK_NAME,
            severity='info',
            title='No domains configured for TLS check',
            detail='Add domains to the scan profile to enable TLS certificate checks.',
            raw={},
        )]

    for domain in domains:
        raw = {'domain': domain}
        info = _get_cert_info(domain)

        if info['error']:
            findings.append(Finding(
                check_name=CHECK_NAME,
                severity='warning',
                title=f'{domain}: TLS check failed',
                detail=info['error'],
                raw={**raw, 'error': info['error']},
            ))
            continue

        cert = info['cert']
        not_after = cert.get('notAfter', '')
        raw['not_after'] = not_after

        try:
            days = _days_until_expiry(not_after)
            raw['days_to_expiry'] = days

            if days < 0:
                findings.append(Finding(
                    check_name=CHECK_NAME,
                    severity='critical',
                    title=f'{domain}: TLS certificate EXPIRED {abs(days)} days ago',
                    detail=f'Certificate expired on {not_after}. Renew immediately.',
                    raw=raw,
                ))
            elif days < 14:
                findings.append(Finding(
                    check_name=CHECK_NAME,
                    severity='critical',
                    title=f'{domain}: TLS certificate expires in {days} days',
                    detail=f'Certificate will expire on {not_after}. Renew urgently.',
                    raw=raw,
                ))
            elif days < 30:
                findings.append(Finding(
                    check_name=CHECK_NAME,
                    severity='warning',
                    title=f'{domain}: TLS certificate expires in {days} days',
                    detail=f'Certificate expires on {not_after}. Plan renewal soon.',
                    raw=raw,
                ))
            else:
                findings.append(Finding(
                    check_name=CHECK_NAME,
                    severity='ok',
                    title=f'{domain}: TLS certificate valid for {days} more days',
                    detail=f'Certificate expires on {not_after}.',
                    raw=raw,
                ))
        except ValueError as e:
            findings.append(Finding(
                check_name=CHECK_NAME,
                severity='warning',
                title=f'{domain}: could not parse certificate expiry',
                detail=str(e),
                raw=raw,
            ))
            continue

        if not _cert_covers_domain(cert, domain):
            findings.append(Finding(
                check_name=CHECK_NAME,
                severity='warning',
                title=f'{domain}: certificate CN/SAN does not match domain',
                detail=f'The TLS certificate does not include {domain} in its names.',
                raw=raw,
            ))

    return findings
