import pandas as pd
import matplotlib.pyplot as plt
import math
from scipy.stats import norm

from .patient import Patient
from pathlib import Path


DATA_PATH = Path(__file__).parent / "data"


class Child(Patient):
    """
    Represents a child patient (2–20 years) and provides growth chart analysis
    and visualization based on CDC growth standards.

    This class extends the Patient class and adds functionality for:
    - Weight-for-age
    - Length-for-age
    - BMI-for-age

    Supports plotting percentile curves and calculating Z-scores and percentiles
    using the LMS method.
    """

    # Child 2 to 20 database
    df_wtage = pd.read_csv(DATA_PATH /'wtage.csv', dtype={'Sex': str})
    df_lenage = pd.read_csv(DATA_PATH /'lenage.csv', dtype={'Sex': str})
    df_bmiage = pd.read_csv(DATA_PATH /'bmiage.csv', dtype={'Sex': str})

    def __init__(self, sex: str, age: int, wt: float = None, length: float = None):
        """
        Initialize a Child object.

        At least one anthropometric measurement must be provided.

        Args:
            sex (str): Child sex.
            age (int): Age in months.
            wt (float, optional): Weight in kilograms.
            length (float, optional): Length/height in centimeters.

        Raises:
            ValueError: If both wt and length are None.
        """
        if wt is None and length is None:
            raise ValueError("At least wt or length must be provided")

        super().__init__(sex, age, wt, length)

    def plot_wtage(self, title=None):
        """
        Plot weight-for-age growth chart with patient's measurement.

        Args:
            title (str, optional): Plot title.
        """
        df = self.df_wtage

        self.sex = str(self.sex).lower()
        if self.sex in ['m', '1']:
            self.sex = '1'
        elif self.sex in ['f', '2']:
            self.sex = '2'
        else:
            raise TypeError('define sex as m or f')

        data = df[df['Sex'] == self.sex]

        plt.figure(figsize=(12, 7))
        for i, col in enumerate(self.agecolumns[1:]):
            if i in self.highlight_indices:
                plt.plot(data['Agemos'], data[col],
                         color='gray', linewidth=2, label=f'{title} {col}')
            else:
                plt.plot(data['Agemos'], data[col],
                         color='lightgray', linewidth=1)

        if self.wt is not None:
            plt.scatter(self.age, self.wt, color='red', s=100, zorder=5)

        plt.title(title)
        plt.xlabel("age(month)")
        plt.ylabel("Weight(Kg)")
        plt.grid(True)
        plt.legend()
        plt.show()

    def plot_lenage(self, title=None):
        """
        Plot length-for-age growth chart with patient's measurement.

        Args:
            title (str, optional): Plot title.
        """
        df = self.df_lenage

        self.sex = str(self.sex).lower()
        if self.sex in ['m', '1']:
            self.sex = '1'
        elif self.sex in ['f', '2']:
            self.sex = '2'
        else:
            raise TypeError('define sex as m or f')

        data = df[df['Sex'] == self.sex]

        plt.figure(figsize=(14, 10))
        for i, col in enumerate(self.agecolumns[1:]):
            if i in self.highlight_indices:
                plt.plot(data['Agemos'], data[col],
                         color='gray', linewidth=2, label=f'{title} {col}')
            else:
                plt.plot(data['Agemos'], data[col],
                         color='lightgray', linewidth=1)

        if self.length is not None:
            plt.scatter(self.age, self.length, color='red', s=100, zorder=5)

        plt.title(title)
        plt.xlabel("age(month)")
        plt.ylabel("Length(cm)")
        plt.grid(True)
        plt.legend()
        plt.show()

    def plot_bmiage(self, title=None):
        """
        Plot BMI-for-age growth chart with patient's measurement.

        Args:
            title (str, optional): Plot title.
        """
        df = self.df_bmiage

        self.sex = str(self.sex).lower()
        if self.sex in ['m', '1']:
            self.sex = '1'
        elif self.sex in ['f', '2']:
            self.sex = '2'
        else:
            raise TypeError('define sex as m or f')

        data = df[df['Sex'] == self.sex]

        plt.figure(figsize=(12, 7))
        for i, col in enumerate(self.agecolumns[1:]):
            if i in self.highlight_indices:
                plt.plot(data['Agemos'], data[col],
                         color='gray', linewidth=2, label=f'{title} {col}')
            else:
                plt.plot(data['Agemos'], data[col],
                         color='lightgray', linewidth=1)

        if self.wt is not None and self.length is not None and self.length > 0:
            plt.scatter(self.age, self.bmi, color='red', s=100, zorder=5)

        plt.title(title)
        plt.xlabel("age(month)")
        plt.ylabel("BMI")
        plt.grid(True)
        plt.legend()
        plt.show()

    def analyze_wtage(self):
        """
        Analyze weight-for-age using LMS method.

        Returns:
            dict: Z-score and percentile.
        """
        df = self.df_wtage
        self.sex = str(self.sex).lower()
        if self.sex in ['m', '1']:
            self.sex = '1'
        elif self.sex in ['f', '2']:
            self.sex = '2'
        else:
            raise TypeError('define sex as m or f')

        df2 = df[df['Sex'] == self.sex]
        row = df2.iloc[(df2['Agemos'] - self.age).abs().argsort()[:1]]

        L = float(row['L'].values[0])
        M = float(row['M'].values[0])
        S = float(row['S'].values[0])

        if L != 0:
            z = ((self.wt / M) ** L - 1) / (L * S)
        else:
            z = math.log(self.wt / M) / S

        percentile = norm.cdf(z) * 100

        return {
            "Z": round(z, 2),
            "Percentile": round(percentile, 2)
        }

    def analyze_lenage(self):
        """
        Analyze length-for-age using LMS method.

        Returns:
            dict: Z-score and percentile.
        """
        df = self.df_lenage
        self.sex = str(self.sex).lower()
        if self.sex in ['m', '1']:
            self.sex = '1'
        elif self.sex in ['f', '2']:
            self.sex = '2'
        else:
            raise TypeError('define sex as m or f')

        df2 = df[df['Sex'] == self.sex]
        row = df2.iloc[(df2['Agemos'] - self.age).abs().argsort()[:1]]

        L = float(row['L'].values[0])
        M = float(row['M'].values[0])
        S = float(row['S'].values[0])

        if L != 0:
            z = ((self.length / M) ** L - 1) / (L * S)
        else:
            z = math.log(self.length / M) / S

        percentile = norm.cdf(z) * 100

        return {
            "Z": round(z, 2),
            "Percentile": round(percentile, 2)
        }

    def analyze_bmiage(self):
        """
        Analyze BMI-for-age using LMS method.

        Returns:
            dict: Z-score and percentile.
        """
        df = self.df_bmiage
        self.sex = str(self.sex).lower()
        if self.sex in ['m', '1']:
            self.sex = '1'
        elif self.sex in ['f', '2']:
            self.sex = '2'
        else:
            raise TypeError('define sex as m or f')

        df2 = df[df['Sex'] == self.sex]
        row = df2.iloc[(df2['Agemos'] - self.age).abs().argsort()[:1]]

        L = float(row['L'].values[0])
        M = float(row['M'].values[0])
        S = float(row['S'].values[0])

        if L != 0:
            z = ((self.bmi / M) ** L - 1) / (L * S)
        else:
            z = math.log(self.bmi / M) / S

        percentile = norm.cdf(z) * 100

        return {
            "Z": round(z, 2),
            "Percentile": round(percentile, 2)
        }
