import logging
from typing import List, Literal, Optional, Union

from pydantic import (BaseModel, ConfigDict, Field, PositiveInt, StrictBool,
                      TypeAdapter, validator)

from .schema_utils import group, is_sorted

log = logging.getLogger('Validation')

PROXIED_VALUES = Literal['false', 'true']
TYPES_VALUES = Literal[
    'A',
    'AAAA',
    'CNAME',
    'MX',
    'TXT',
    'CAA',
    'SRV',
    'PTR',
    'SOA',
    'NS',
    'DS',
    'DNSKEY',
    'LOC',
    'NAPTR',
    'SSHFP',
    'SVCB',
    'TSLA',
    'URI',
    'SPF',
]


def check_duplicates(records):
    print(records)
    grouped = group(records, lambda r: r.name)
    grouped = {k: v for k, v in grouped.items() if len(v) > 1}
    if not grouped:
        return
    duplicates = []
    for name, records in grouped.items():
        # Some entries can be duplicated
        types = {r.type for r in records}
        types -= {'MX', 'TXT'}  # They can be added along with any other
        # A and AAAA records are compatible
        if any(t in types for t in ('A', 'AAAA')):
            types -= {'A', 'AAAA'}
            if types:
                duplicates.append(name)
        # We can have multiple value for NS records
        if 'NS' in types and len(types) > 1:
            duplicates.append(name)
    if not duplicates:
        log.warn(('There are duplicate entries,'
                  'be sure that it is what you want:\n{}').format('\n'.join(
                      grouped.keys())))
        return
    duplicates_str = '\n'.join(duplicates)
    msg = f"""\
The following records have duplicates
Note that some records can be duplicated (e.g. A, AAAA, NS),
and some types are compatibles (e.g. A and AAAA):
{duplicates_str}
"""
    log.error(msg)
    raise Exception(msg)


# Same schema to validate tfplan.json, Cloudflare output and the csv file
class Record(BaseModel):
    # https://docs.pydantic.dev/latest/api/config/#pydantic.config.ConfigDict.populate_by_name
    model_config = ConfigDict(populate_by_name=True)

    id: Optional[str] = None
    name: str
    type: TYPES_VALUES
    value: str = Field(alias='content')
    ttl: PositiveInt
    proxied: Union[StrictBool, PROXIED_VALUES]

    @validator('name')
    def no_trailing_dot(cls, value):
        if value.strip().endswith('.'):
            raise ValueError('dns entry are not allowed to end with a "."')
        return value

    def get_uuid(self):
        return (self.name, self.value)

    def __str__(self):
        return '{}, {}, {}'.format(self.name, self.type, self.value)


RecordList = TypeAdapter(List[Record])


def check_records(records):
    records = RecordList.validate_python(records)  # model_validate
    check_duplicates(records)
    if not is_sorted(records, key=lambda x: x.name):
        log.warn('Records are not sorted, please sort them')
    return records


def sort_records(records):
    yield from sorted(records,
                      key=lambda r: (r['name'], r['type'], r['content']))
