import requests
import pandas as pd
import requests
from datetime import datetime, timedelta
from stockstats import StockDataFrame
from datetime import datetime, timedelta
import pandas as pd
from stockstats import StockDataFrame
from ..indicators import days_ago

from datetime import datetime
import pytz  # Necesario para manejar zonas horarias
from stockstats import StockDataFrame  # Si se


def add_days_ago(data, days):
    for i in range(1, days + 1):
        data[f'd{i}'] = data['close'].shift(i)
    data.dropna(inplace=True)
    return data

class OpensearchCollector:

    def __init__(self,
            url_opensearch: str = "http://localhost:9200",
            url_playground: str = "http://localhost:5005",
            time_amount :str = 10,
            time_unit :str = 'Minute'
            ):

        self.url_opensearch = url_opensearch
        self.url_playground = url_playground
        self.time_amount = time_amount
        self.time_unit = time_unit


    def get_latest_data(self, symbol):
        
        symbol_param = symbol.replace('/', '_')
        url = f"{self.url_opensearch}/{symbol_param.lower()}/_search"

        query = {
            "sort": [
                {"timestamp": {"order": "desc"}}
            ]
        }

        response = requests.get(url, json=query)

        if response.status_code == 200:
            data = response.json()
            hits = data.get("hits", {}).get("hits", [])

            if hits:
                latest_data = hits[0]["_source"]
                df = pd.DataFrame([latest_data])
                date = df['timestamp'][0] 
                date_object = datetime.strptime(date, "%Y-%m-%dT%H:%M:%S%z")


                return date_object
            else:
                print("No se encontraron datos para el rango especificado.")
                return None
        else:
            print(f"Error: {response.status_code}, {response.text}")
            return None


    def data_market_collect(self, symbol, interval, start_date, end_date):
        symbol_param = symbol.replace('/', '_')
        url = f"{self.url_opensearch}/{symbol_param.lower()}/_search"
        # "2023-11-01T09:00:00+00:00",
        query = {
            "query": {
                "range": {
                "timestamp": {
                "gte": start_date.strftime('%Y-%m-%dT%H:%M:%S+00:00'),
                "lt": end_date.strftime('%Y-%m-%dT%H:%M:%S+00:00')
                }
            }
            },
            "aggs": {
                "custom": {
                    "date_histogram": {
                        "field": "timestamp",
                        "interval": interval,
                        "format": "yyyy-MM-dd HH:mm:ss"
                    },
                    "aggs": {
                        "open": {
                            "avg": {
                                "field": "open"
                            }
                        },
                        "high": {
                            "max": {
                                "field": "high"
                            }
                        },
                        "low": {
                            "min": {
                                "field": "low"
                            }
                        },
                        "close": {
                            "avg": {
                                "field": "close"
                            }
                        },
                        "volume": {
                            "sum": {
                                "field": "volume"
                            }
                        },
                        "trade_count": {
                            "sum": {
                                "field": "trade_count"
                            }
                        }
                    }
                }
            }
        }

        response = requests.get(url, json=query)

        if response.status_code == 200:
            data = response.json()
            aggregations = data.get("aggregations", {})
            custom = aggregations.get("custom", {})
            buckets =custom.get("buckets", [])

            data_list = []
            for bucket in buckets:
                data_dict = {
                    "timestamp": bucket.get("key_as_string"),
                    "open": bucket.get("open", {}).get("value"),
                    "high": bucket.get("high", {}).get("value"),
                    "low": bucket.get("low", {}).get("value"),
                    "close": bucket.get("close", {}).get("value"),
                    "volume": bucket.get("volume", {}).get("value"),
                    "trade_count": bucket.get("trade_count", {}).get("value")
                }
                data_list.append(data_dict)

            df = pd.DataFrame(data_list)

            print(f'---- {symbol} {interval} {len(df)}')

            if len(df) > 0:
                df = df.dropna(subset=['open'])
                df = df.rename(columns={'timestamp': 'date'})
                df['symbol'] = symbol
            return df
        else:
            print(f"Error: {response.status_code}, {response.text}")
            return None

    def save_data_opensearch(self, symbols, start_date, end_date, time_amount, time_unit, data_type, is_crypto):
        
        url = f"{self.url_playground}/execute"
        if not is_crypto:
            data_type = 'stock'
        else: 
            data_type = 'crypto'
        
        params = {
            "symbols": symbols,
            "start_date.year": start_date.year,
            "start_date.month": start_date.month,
            "start_date.day": start_date.day,
            "start_date.hour": start_date.hour,
            "start_date.minute": start_date.minute,
            "end_date.year": end_date.year,
            "end_date.month": end_date.month,
            "end_date.day": end_date.day,
            "end_date.hour": end_date.hour,
            "end_date.minute": end_date.minute,
            "time_amount": time_amount,
            "time_unit": time_unit,
            "data_type": data_type
        }
        
        headers = {'accept': 'application/json'}

        response = requests.get(url, params=params, headers=headers)

        if response.status_code == 200:
            data = response.json()
            return data
        else:
            print(f"Error: {response.status_code}, {response.text}")
            return None

    def market_data_days_ago(self, delay_minutes: int = 80, search_days_ago :int = 300, symbol :str = 'AMZN', data_type='stock', interval='1h', is_crypto=False):

        now_datetime = datetime.now()
        delta = timedelta(minutes=delay_minutes)
        delta_month = timedelta(days=search_days_ago)
        end_date = now_datetime - delta
        start_date = now_datetime - delta_month
        start_date_request = start_date

        latest_date_ops = self.get_latest_data(symbol=symbol)
        if isinstance(latest_date_ops, datetime):
            start_date = latest_date_ops

        self.save_data_opensearch(
            symbols=symbol, 
            start_date=start_date,
            end_date=end_date, 
            time_amount=self.time_amount, 
            time_unit=self.time_unit, 
            data_type=data_type,
            is_crypto=is_crypto
        )


        data = self.data_market_collect(
            symbol=symbol, 
            interval=interval,
            start_date=start_date_request,  
            end_date=end_date
            )



        return data
    

    def market_data(self, symbols: str = 'AMZN', start_date: str = '6/1/2021', end_date: str = '12/1/2021', interval: str = '1h', points_ago: int = 4, is_crypto=False):
        # Convertir las fechas de string a objetos datetime
        start_date = datetime.strptime(start_date, '%m/%d/%Y') if isinstance(start_date, str) else start_date
        end_date = datetime.strptime(end_date, '%m/%d/%Y') if isinstance(end_date, str) else end_date

        # Asegurarse de que las fechas sean 'offset-naive' o 'offset-aware' de manera consistente
        # Aquí se asume que quieres trabajar con 'offset-naive' datetimes
        if start_date.tzinfo is not None:
            start_date = start_date.replace(tzinfo=None)
        if end_date.tzinfo is not None:
            end_date = end_date.replace(tzinfo=None)
        start_date_required = start_date
        data_list = []  # Lista para almacenar los DataFrames individuales

        if not isinstance(symbols,list):
            symbols = [symbols]
        for symbol in symbols:  # Asegúrate de que symbols es un string
            # Obtener la última fecha disponible de los datos
            latest_date_ops = self.get_latest_data(symbol=symbol)
            if isinstance(latest_date_ops, datetime):
                # Asegurarse de que latest_date_ops sea 'offset-naive' para la comparación
                if latest_date_ops.tzinfo is not None:
                    latest_date_ops = latest_date_ops.replace(tzinfo=None)
                start_date = max(start_date, latest_date_ops)

            # Guardar datos en OpenSearch
            self.save_data_opensearch(
                symbols=symbol, 
                start_date=start_date,
                end_date=end_date, 
                time_amount=self.time_amount, 
                time_unit=self.time_unit, 
                data_type='stock',
                is_crypto=is_crypto
            )

            # Obtener datos del mercado
            data = self.data_market_collect(
                symbol=symbol, 
                interval=interval,
                start_date=start_date_required,  
                end_date=end_date
            )


            if hasattr(self, 'indicators') and self.indicators:
                data = StockDataFrame.retype(data)
                for indicator in self.indicators:
                    data[indicator]

            data_list.append(data)  # Agregar cada DataFrame a la lista

        data_pd = pd.concat(data_list, ignore_index=True)
        data_pd['date'] = pd.to_datetime(data_pd['date'])
        return  data_pd


    def market_data_old(self, symbols :str = 'AMZN', start_date='6/1/2021', end_date='12/1/2021', interval='1h', points_ago :int =4):


        data_list = []  # Lista para almacenar los DataFrames individuales

        self.points_ago = 5
        self.indicators = ['boll_ub', 'boll_lb', 'boll']

        for symbol in symbols:
            print(f'---------------- {str(start_date)}')
            print(f'---------------- {str(end_date)}')

            self.save_data_opensearch(
                symbols=symbol, 
                start_date=start_date,
                end_date=end_date, 
                time_amount=self.time_amount, 
                time_unit=self.time_unit, 
                data_type='stock'
            )

            # Resto del código para obtener datos
            data = self.data_market_collect(
                symbol=symbol, 
                interval=interval,
                start_date=start_date,  
                end_date=end_date
            )


            if self.points_ago:
                data = add_days_ago(data=data, days=self.points_ago)
            if self.indicators:
                data = StockDataFrame.retype(data)
        
            data_list.append(data)  # Agregar cada DataFrame a la lista
            
            data = days_ago(data=data, days=5)
            data_list.append(data)  # 


            for indicator in self.indicators:
                data[indicator]

        if data_list:
            concatenated_data = pd.concat(data_list, ignore_index=True)
            concatenated_data['date'] = pd.to_datetime(concatenated_data['date'])

            return concatenated_data
        else:
            return None

