import re
from typing import Any, Optional, List, Dict

from pydantic import BaseModel, Field, field_validator
from typing_extensions import Annotated
import logging
from app.schemas import DataCollectionParameters

logging.basicConfig(level=logging.DEBUG)
logger = logging.getLogger(__name__)


class SpreadsheetModel(BaseModel):
    dewarname: str = Field(..., alias="dewarname")
    puckname: str = Field(..., alias="puckname")
    pucktype: Optional[str] = Field(None, alias="pucktype")
    crystalname: Annotated[
        str,
        Field(
            ...,
            max_length=64,
            title="Crystal Name",
            description="max_length imposed by MTZ file header"
            "format https://www.ccp4.ac.uk/html/mtzformat.html",
            alias="crystalname",
        ),
    ]
    positioninpuck: int  # Only accept positive integers between 1 and 16
    priority: Optional[int]
    comments: Optional[str]
    proteinname: Optional[str] = ""  # Alphanumeric validation
    data_collection_parameters: Optional[DataCollectionParameters] = None

    # Add pucktype validation
    @field_validator("pucktype", mode="before")
    @classmethod
    def validate_pucktype(cls, v):
        if v != "unipuck":
            raise ValueError(f"'{v}' is not valid. Pucktype must be 'unipuck'.")
        return v

    # Validators
    @field_validator("dewarname", "puckname", mode="before")
    @classmethod
    def dewarname_puckname_characters(cls, v):
        if v:
            v = str(v).strip().replace(" ", "_").upper()
            if re.search("\n", v):
                assert v.isalnum(), "is not valid. newline character detected."
            v = re.sub(r"\.0$", "", v)
            return v
        raise ValueError("Value must be provided for dewarname and puckname.")

    @field_validator("crystalname", mode="before")
    @classmethod
    def parameter_characters(cls, v):
        v = str(v).replace(" ", "_")
        if re.search("\n", v):
            assert v.isalnum(), "is not valid. newline character detected."
        characters = re.sub("[._+-]", "", v)
        assert (
            characters.isalnum()
        ), f" '{v}' is not valid. Only alphanumeric and . _ + - characters allowed."
        return v

    @field_validator("positioninpuck", mode="before")
    @classmethod
    def positioninpuck_possible(cls, v):
        if not isinstance(v, int) or v < 1 or v > 16:
            raise ValueError(
                f" '{v}' is not valid. Value must be an integer between 1 and 16."
            )
        return v

    @field_validator("priority", mode="before")
    @classmethod
    def priority_positive(cls, v):
        if v is not None:
            try:
                v = int(v)
                if v <= 0:
                    raise ValueError(
                        f" '{v}' is not valid. Value must be a positive integer."
                    )
            except (ValueError, TypeError) as e:
                raise ValueError(
                    f" '{v}' is not valid. Value must be a positive integer."
                ) from e
        return v

        # if not v:  # Handles None or empty cases
        #    default_value = "{sgPuck}/{sgPosition}"
        #    logger.warning(
        #        f"'directory' field is empty or None. Assigning default value: "
        #        f"{default_value}"
        #    )
        #    return default_value

    # class TELLModel(SpreadsheetModel):
    #    input_order: int
    #    samplemountcount: int = 0
    #    samplestatus: str = "not present"
    #    puckaddress: str = "---"
    #    username: str
    #    puck_number: int
    #    prefix: Optional[str]
    #    folder: Optional[str]


class SpreadsheetResponse(BaseModel):
    data: List[SpreadsheetModel]  # Validated data rows as SpreadsheetModel instances
    errors: List[Dict[str, Any]]  # Errors encountered during validation
    raw_data: List[
        Dict[str, Any]
    ]  # perhaps this has to be changed with th actual model !
    addinfo: List[Dict[str, Any]]
    dewars_count: int
    dewars: List[str]
    pucks_count: int
    pucks: List[str]
    samples_count: int
    samples: List[str]
    headers: Optional[List[str]] = None  # Add headers if needed


__all__ = ["SpreadsheetModel", "SpreadsheetResponse"]
