from operator import itemgetter
from typing import Iterator, List

from marshmallow import (
    Schema,
    ValidationError,
    fields,
    post_load,
    validate,
    validates,
    validates_schema,
)


class ClientValidDataSchema(Schema):
    first_name = fields.String(
        validate=validate.Length(
            min=1, max=256, error="Имя не должно превышать 256 символов."
        ),
        required=False,
        allow_none=True,
    )
    last_name = fields.String(
        validate=validate.Length(
            min=1, max=256, error="Фамилия не должна превышать 256 символов."
        ),
        required=False,
        allow_none=True,
    )
    comment = fields.String(required=False, allow_none=True)
    email = fields.String(
        validate=[
            validate.Length(
                min=1,
                max=64,
                error="Адрес электронной почты не должен превышать 64 символа.",
            ),
            validate.Email(error="Некорректный адрес электронной почты."),
        ],
        required=False,
        allow_none=True,
    )
    phone = fields.String(required=False, allow_none=True)

    @post_load
    def _normalize_phone(self, data: dict) -> dict:
        if data.get("phone"):
            data["phone"] = int("".join(ch for ch in data["phone"] if ch.isdigit()))

        return data

    @validates("phone")
    def _validate_phone(self, value: str) -> None:
        if value:
            if any(c.isalpha() for c in value):
                raise ValidationError("Номер телефона не должен содержать буквы.")

            phone_numbers = "".join(ch for ch in value if ch.isdigit())
            if len(phone_numbers) < 3 or len(phone_numbers) > 16:
                raise ValidationError(
                    "Номер телефона должен содержать от 3 до 16 цифр."
                )

    @validates_schema
    def _validate_identity_fields(self, data: dict) -> None:
        if not any([data.get("phone"), data.get("email")]):
            raise ValidationError(
                "Нужен как минимум один контакт: почта или телефон.",
                field_names=["contacts"],
            )


class Validator:
    def __init__(self):
        self._schema = ClientValidDataSchema()

    def validate_data(
        self, parsed_data: Iterator[List[str]], markup: List[dict]
    ) -> tuple:
        valid_clients = []
        invalid_clients = []

        for cells in parsed_data:
            client_data, errors = self._schema.load(
                self._normalize_data(cells=cells, markup=markup)
            )
            if errors:
                invalid_clients.append(dict(row=";".join(cells), reason=errors))
            else:
                valid_clients.append(client_data)

        return valid_clients, invalid_clients

    def _normalize_data(self, markup: List[dict], cells: List[str]) -> dict:
        client = dict()
        for mk in markup:
            column_type, column_number = itemgetter("column_type", "column_number")(mk)
            try:
                client[column_type.value] = (
                    cells[column_number] if cells[column_number] else None
                )
            except IndexError:
                client[column_type.value] = None

        return client
