import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import math
from sklearn.linear_model import LinearRegression


class Horner:
    def __init__(self, tp=None, filePath=None):
        self.tp = tp
        self.filePath = filePath
        if self.filePath is not None:
            self.p_column, self.t_column = self.name_column_csv()
            self.df = pd.read_csv(self.filePath)
            self.p = self.df[self.p_column]
            self.t = self.df[self.t_column]
        else:
            print("Invalid file path. Please provide a valid file path.")
            exit()

    def name_column_csv(self):
        p_column = "p"
        t_column = "t"
        return p_column, t_column

    def horner_plot(self):
        if self.tp is None:
            print("Please provide a shut-in time (tp).")
            exit()

        horner_list = [
            (self.tp + self.t[i]) / self.t[i]
            for i in range(len(self.t))
            if self.t[i] != 0
        ]
        plt.semilogx(horner_list, self.p[1:], marker="o", ls="", color="k", ms=4)
        plt.grid(True, which="both", ls="--")
        return horner_list, self.p[1:]


class BuildUpCalculatorParameter(Horner):
    """
    Class to calculate permeability, skin factor, and other relevant parameters.
    """

    def __init__(
        self,
        filePath=None,
        tp=None,
        Qo=None,
        Bo=None,
        mo=None,
        Ø=None,
        Ct=None,
        rw=None,
        h=None,
    ):
        """
        Initializes the BuildUpCalculatorParameter object.

        Args:
            tp (float): Shut-in time.
            filePath (str): Path to the CSV file containing pressure and time data.
            Qo (float): Oil production rate.
            Bo (float): Oil formation volume factor.
            mo (float): Oil viscosity.
            Ø (float): Porosity.
            Ct (float): Total compressibility.
            rw (float): Wellbore radius.
            h (float): Formation thickness.
        """
        super().__init__(tp=tp, filePath=filePath)
        self.Qo = Qo
        self.Bo = Bo
        self.mo = mo
        self.Ø = Ø
        self.Ct = Ct
        self.rw = rw
        self.h = h

    @property
    def Ph0(self):
        """
        Returns:
            float: Initial pressure.
        """
        return self.p[0]

    def Ph1(self, slope=None, intercept=None):
        """
        Returns:
            float: Pressure at shut-in time (P1hr).
        """
        if slope is None or intercept is None:
            raise ValueError("Slope and intercept must be provided.")
        return intercept + (slope * math.log(self.tp + 1))

    def k(self, slope):
        _K_CONSTANT = 162.6  # Constant for calculating permeability
        """
        Returns:
            float: Permeability (k).
        """
        if self.Qo is None or self.Bo is None or self.mo is None or self.h is None:
            raise ValueError("Qo, Bo, mo, and h must be provided.")
        kh = _K_CONSTANT * self.Qo * self.Bo * self.mo / slope
        return abs(kh / self.h)

    def skin(self, intercept, slope, k):
        """
        Returns:
            float: Skin factor (s).
        """
        P1hr = self.Ph1(slope=slope, intercept=intercept)
        if self.Ø is None or self.mo is None or self.Ct is None or self.rw is None:
            raise ValueError("Ø, mo, Ct, and rw must be provided.")
        return (
            (1.151 * ((P1hr - self.Ph0) / slope))
            - math.log10((k / (self.Ø * self.mo * self.Ct * self.rw**2)))
            + 3.23
        )


class InteractivePlot(BuildUpCalculatorParameter):
    def __init__(
        self,
        fig,
        ax,
        filePath=None,
        tp=None,
        Qo=None,
        Bo=None,
        mo=None,
        Ø=None,
        Ct=None,
        rw=None,
        h=None,
    ):
        """
        Initializes the InteractivePlot object.

        Args:
            fig (matplotlib.figure.Figure): The figure object.
            ax (matplotlib.axes.Axes): The axes object.
            filePath (str): Path to the CSV file containing pressure and time data.
            tp (float): Shut-in time.
            Qo (float): Oil production rate.
            Bo (float): Oil formation volume factor.
            mo (float): Oil viscosity.
            Ø (float): Porosity.
            Ct (float): Total compressibility.
            rw (float): Wellbore radius.
            h (float): Formation thickness.
        """
        super().__init__(
            filePath=filePath, tp=tp, Qo=Qo, Bo=Bo, mo=mo, Ø=Ø, Ct=Ct, rw=rw, h=h
        )
        self.fig = fig
        self.ax = ax
        self.points = []
        self.line = None
        self.cid = fig.canvas.mpl_connect("button_press_event", self.on_click)
        self.horner_list, self.pressure_list = self.horner_plot()

    def on_click(self, event):
        """
        Handles click events on the plot.
        """
        if event.inaxes != self.ax:
            return

        # Find the nearest data point
        click_point = (event.xdata, event.ydata)
        nearest_point = self.find_nearest_point(click_point)

        if nearest_point:
            self.points.append(nearest_point)
            self.ax.plot(nearest_point[0], nearest_point[1], marker="x", color="red")

            if len(self.points) == 2:
                slope, intercept = self.perform_linear_regression()
                self.ax.plot(
                    self.horner_list,
                    intercept + slope * np.log(self.horner_list),
                    color="blue",
                )
                plt.draw()
                self.display_results(slope, intercept)

    def find_nearest_point(self, click_point):
        """
        Finds the nearest point in the Horner plot data to the clicked point.

        Args:
            click_point (tuple): The (x, y) coordinates of the click event.

        Returns:
            tuple: The (x, y) coordinates of the nearest data point.
        """
        distances = [
            (hx, hp, self.euclidean_distance(click_point, (hx, hp)))
            for hx, hp in zip(self.horner_list, self.pressure_list)
        ]
        nearest = min(distances, key=lambda t: t[2])
        return (nearest[0], nearest[1])

    @staticmethod
    def euclidean_distance(point1, point2):
        """
        Calculates the Euclidean distance between two points.

        Args:
            point1 (tuple): The (x, y) coordinates of the first point.
            point2 (tuple): The (x, y) coordinates of the second point.

        Returns:
            float: The Euclidean distance between the points.
        """
        return np.sqrt((point1[0] - point2[0]) ** 2 + (point1[1] - point2[1]) ** 2)

    def perform_linear_regression(self):
        """
        Performs linear regression on the selected points.

        Returns:
            tuple: The slope and intercept of the regression line.
        """
        x = np.log([point[0] for point in self.points]).reshape(-1, 1)
        y = [point[1] for point in self.points]
        print ("x",x)
        print ("y",y)
        reg = LinearRegression().fit(x, y)
        slope = reg.coef_[0]
        intercept = reg.intercept_
        return slope, intercept

    def display_results(self, slope, intercept):
        """
        Displays the calculated permeability and skin factor.

        Args:
            slope (float): The slope of the regression line.
            intercept (float): The intercept of the regression line.
        """
        intercept
        k = self.k(slope)
        s = self.skin(intercept, abs(slope), k)
        print(f"Permeability (k): {k:.4f} mD")
        print(f"Skin factor (s): {s:.4f}")


fil = r"C:\Users\pc\Desktop\projects\oil_and_gas\worked_data.csv"

data = {
    "Tp": 15724.26,
    "Qo": 542,
    "μο": 0.108,
    "Ø": 0.031,
    "Ct": 3.91e-5,
    "Bo": 2.5052,
    "rw": 0.245,
    "h": 787,
    "Phr=0": 1873.77,
    "P1hr": 4648,
    "Re": 849,
}


Qo = data["Qo"]
Bo = data["Bo"]
mo = data["μο"]
h = data["h"]
Ø = data["Ø"]
Ct = data["Ct"]
rw = data["rw"]
Phr_0 = data["Phr=0"]
P1hr = data["P1hr"]
Re = data["Re"]
tp = data["Tp"]

# Example usage:
file_path = fil
# Example shut-in time in hours
# Example oil production rate in STB/day
# Example oil formation volume factor in bbl/STB
# Example oil viscosity in cp
# Example porosity in fraction
# Example total compressibility in 1/psi
# Example wellbore radius in feet
# Example formation thickness in feet

# Create a figure and axis for the plot
fig, ax = plt.subplots()

# Initialize the interactive plot
interactive_plot = InteractivePlot(
    fig, ax, filePath=file_path, tp=tp, Qo=Qo, Bo=Bo, mo=mo, Ø=Ø, Ct=Ct, rw=rw, h=h
)

# Display the plot
plt.show()
Adding an option that enables you to modify the points that were added to the main chart