import pandas as pd
from abc import ABC, abstractmethod


class AbstractNormalizer(ABC):
    @abstractmethod
    def _get_producer_list() -> list[str]:
        pass

    @abstractmethod
    def _normalize_cost() -> pd.DataFrame:
        pass

    @abstractmethod
    def _normalize_cost_estimate() -> pd.DataFrame:
        pass

    @abstractmethod
    def _normalize_quality() -> pd.DataFrame:
        pass

    @abstractmethod
    def _normalize_quality_estimate() -> pd.DataFrame:
        pass

    @abstractmethod
    def _normalize_train() -> pd.DataFrame:
        pass

    @abstractmethod
    def _normalize_test() -> pd.DataFrame:
        pass

    def normalize_and_write(self) -> None:
        self.cost = self._normalize_cost()
        self.logger.info(f"Cost dataset columns: {self.cost.columns.tolist()}.")
        self.cost.to_csv(self.config["cost"]["output_path"], index=False, header=True)

        self.cost_estimate = self._normalize_cost_estimate()
        self.logger.info(
            f"Cost-estimate dataset columns: {self.cost_estimate.columns.tolist()}."
        )
        try:
            assert self.cost.shape == self.cost_estimate.shape
        except AssertionError:
            self.logger.error(
                f"Cost dataset and cost-estimate dataset have different shapes: {self.cost.shape} != {self.cost_estimate.shape}."
            )
            raise AssertionError(
                "Cost dataset and cost-estimate dataset have different shapes!"
            )
        self.cost_estimate.to_csv(
            self.config["cost_estimate"]["output_path"], index=False, header=True
        )

        self.quality = self._normalize_quality()
        self.logger.info(
            f"Quality dataset columns: {self.quality.columns.tolist()}, and shape: {self.quality.shape}."
        )
        try:
            assert self.quality.shape == self.cost_estimate.shape
        except AssertionError:
            self.logger.error(
                f"Quality dataset and cost-estimate dataset have different shapes: {self.quality.shape} != {self.cost_estimate.shape}."
            )
            raise AssertionError(
                "Quality dataset and cost-estimate dataset have different shapes!"
            )
        self.quality.to_csv(
            self.config["quality"]["output_path"], index=False, header=True
        )

        self.quality_estimate = self._normalize_quality_estimate()
        self.logger.info(
            f"Quality-estimate dataset columns: {self.quality_estimate.columns.tolist()}, and shape: {self.quality_estimate.shape}."
        )
        try:
            assert self.quality_estimate.shape == self.quality.shape
        except AssertionError:
            self.logger.error(
                f"Quality-estimate dataset and quality dataset have different shapes: {self.quality_estimate.shape} != {self.quality.shape}."
            )
            # Do not raise AssertionError: quality estimates are only available for the test dataset.
        self.quality_estimate.to_csv(
            self.config["quality_estimate"]["output_path"], index=False, header=True
        )

        self.train = self._normalize_train()
        self.logger.info(
            f"Train dataset columns: {self.train.columns.tolist()}, and shape: {self.train.shape}."
        )
        self.train.to_csv(self.config["train"]["output_path"], index=False, header=True)

        self.test = self._normalize_test()
        self.logger.info(
            f"Test dataset columns: {self.test.columns.tolist()}, and shape: {self.test.shape}."
        )
        self.test.to_csv(self.config["test"]["output_path"], index=False, header=True)
