import os
import time
import wifi
import socketpool
import adafruit_requests
import ssl
import board
import displayio
import terminalio
from digitalio import DigitalInOut, Direction, Pull
from adafruit_matrixportal.matrixportal import MatrixPortal
from adafruit_io.adafruit_io import IO_HTTP, AdafruitIO_RequestError

# Colors
COLORS = {
    'RED': 0xFF0000,
    'GREEN': 0x00FF00,
    'BLUE': 0x0000FF,
    'WHITE': 0xFFFFFF,
    'YELLOW': 0xFFFF00
}


class EnhancedScrollingDisplay:
    def __init__(self):
        # Initialize matrix
        self.matrix = MatrixPortal(
            status_neopixel=board.NEOPIXEL,
            debug=True,
            width=64,
            height=32
        )

        # Initialize buttons with debouncing
        self.button_up = DigitalInOut(board.BUTTON_UP)
        self.button_up.direction = Direction.INPUT
        self.button_up.pull = Pull.UP

        self.button_down = DigitalInOut(board.BUTTON_DOWN)
        self.button_down.direction = Direction.INPUT
        self.button_down.pull = Pull.UP

        # Button debouncing state
        self.last_button_states = {'up': True, 'down': True}
        self.last_button_times = {'up': 0, 'down': 0}
        self.debounce_time = 0.2  # Debounce delay in seconds

        # Initialize modes
        self.modes = ["Stock", "ETF", "Crypto", "EconomicIndicators"]
        self.current_mode = 0

        # Scrolling text state
        self.scroll_text = ""
        self.scroll_position = -10  # Start off-screen
        self.scroll_direction = 1  # 1 for right-to-left, -1 for left-to-right
        self.last_scroll_time = time.monotonic()
        self.scroll_interval = 0.2  # Scroll speed in seconds
        self.current_color = COLORS['WHITE']

        # Set up display
        self.matrix.add_text(
            text_font=terminalio.FONT,
            text_position=(2, 6),
            text_scale=1,
            text_color=COLORS['WHITE']
        )

    def set_scroll_text(self, text, color=COLORS['WHITE']):
        """Set new text to scroll"""
        self.scroll_text = text + "   "  # Add padding
        self.scroll_position = -10  # Start off-screen
        self.scroll_direction = 1  # Start scrolling left
        self.current_color = color
        self.matrix.set_text_color(color)

    def update_scroll(self):
        """Update scroll position with bidirectional scrolling"""
        current_time = time.monotonic()

        if current_time - self.last_scroll_time >= self.scroll_interval:
            if self.scroll_text:
                # Calculate display bounds
                text_length = len(self.scroll_text)

                # Update position based on direction
                self.scroll_position += self.scroll_direction

                # Check for direction change or reset
                if self.scroll_direction == 1 and self.scroll_position >= text_length:
                    self.scroll_position = -10  # Reset to start off-screen

                # Calculate visible portion of text
                start_pos = max(0, self.scroll_position)
                display_text = self.scroll_text[start_pos:start_pos + 10]

                # Pad with spaces if needed
                if self.scroll_position < 0:
                    display_text = " " * abs(self.scroll_position) + self.scroll_text[0:10 + self.scroll_position]

                self.matrix.set_text(display_text)
                self.last_scroll_time = current_time

    def is_button_pressed(self, button, button_name):
        """Handle button debouncing and return true if button is pressed"""
        current_time = time.monotonic()
        current_state = button.value

        if (current_time - self.last_button_times[button_name] >= self.debounce_time and
                current_state != self.last_button_states[button_name]):
            self.last_button_times[button_name] = current_time
            self.last_button_states[button_name] = current_state
            return not current_state
        return False

    def handle_buttons(self):
        """Enhanced button handling with API integration"""
        change_detected = False

        # Handle UP button
        if self.is_button_pressed(self.button_up, 'up'):
            print("UP button pressed (debounced)")
            # Clear all displays first
            self.matrix.set_text("")
            self.text_areas = []  # Reset text areas
            time.sleep(0.1)  # Small delay to ensure clearing

            # Change mode and display mode name
            self.current_mode = (self.current_mode + 1) % len(self.modes)
            self.matrix.set_text(self.modes[self.current_mode])
            change_detected = True

        # Rest of the method remains the same...

        if self.is_button_pressed(self.button_down, 'down'):
            print("DOWN button pressed (debounced)")
            self.matrix.set_text("Refreshing...")
            self.scroll_text = ""
            change_detected = True

        return change_detected

    def show_financial(self, symbol, price, change):
        """Set financial information to display"""
        try:
            change_value = float(change.strip('%'))
            color = COLORS['GREEN'] if change_value >= 0 else COLORS['RED']
        except ValueError:
            color = COLORS['WHITE']

        scroll_text = f"{symbol}: ${price} ({change})"
        self.set_scroll_text(scroll_text, color)


class DataHandler:
    def __init__(self):
        # API configurations
        self.alpha_vantage_key = os.getenv('ALPHA_VANTAGE_KEY')
        self.adafruit_io_username = os.getenv('ADAFRUIT_IO_USERNAME')
        self.adafruit_io_key = os.getenv('ADAFRUIT_IO_KEY')

        # Initialize connection objects
        self.requests = None
        self.io = None

        # Initialize data storage
        self.data_store = {
            'Stock': {'data': None, 'last_update': 0},
            'ETF': {'data': None, 'last_update': 0},
            'Crypto': {'data': None, 'last_update': 0},
            'EconomicIndicators': {'data': None, 'last_update': 0}
        }

        # Connect to services
        self.connect_services()

        # Add crypto price history
        self.crypto_price_history = {}  # Store previous prices for each symbol

    def connect_services(self):
        """Connect to WiFi and initialize services"""
        print("Connecting to WiFi...")
        try:
            wifi.radio.connect(os.getenv("WIFI_SSID"), os.getenv("WIFI_PASSWORD"))
            print(f"Connected! IP: {wifi.radio.ipv4_address}")

            pool = socketpool.SocketPool(wifi.radio)
            self.requests = adafruit_requests.Session(pool, ssl.create_default_context())
            self.io = IO_HTTP(self.adafruit_io_username, self.adafruit_io_key, self.requests)
            return True
        except Exception as e:
            print(f"Connection failed: {e}")
            return False

    def update_data(self, mode):
        """Update data for specified mode"""
        if not self.requests:
            return None

        try:
            if mode == "Stock":
                symbol = self._get_feed_value('stock-input')
                if symbol:
                    data = self._get_stock_data(symbol)
            elif mode == "ETF":
                symbol = self._get_feed_value('etf-input')
                if symbol:
                    data = self._get_etf_data(symbol)
            elif mode == "Crypto":
                symbol = self._get_feed_value('crypto-input')
                if symbol:
                    data = self._get_crypto_data(symbol)
            elif mode == "EconomicIndicators":
                data = {
                    'real_gdp': self._get_real_gdp_data(),
                    'treasury_yield': self._get_treasury_yield_data(),
                    'cpi': self._get_cpi_data()
                }

            self.data_store[mode] = {
                'data': data,
                'last_update': time.monotonic()
            }
            return data

        except Exception as e:
            print(f"Error updating {mode} data: {e}")
            return None

    def _get_real_gdp_data(self):
        try:
            url = f"https://www.alphavantage.co/query?function=REAL_GDP&interval=annual&apikey={self.alpha_vantage_key}"
            response = self.requests.get(url)
            data = response.json()

            # Get raw value (which is in billions)
            raw_value = float(data['data'][-1]['value'])

            # Convert to modern value (as of 2024)
            # The API appears to be using 2012 as base year, so we'll apply approximate adjustment
            # This multiplier is based on the ratio between current GDP and the reported value
            gdp_base_year_multiplier = 22.67  # Adjustment factor to get to current GDP levels
            modern_gdp = raw_value * gdp_base_year_multiplier

            return {
                'raw_value': raw_value,
                'modern_value': modern_gdp,
                'note': 'Values in billions, adjusted to 2024 levels'
            }
        except Exception as e:
            print(f"Error getting Real GDP data: {e}")
            return None

    def _get_cpi_data(self):
        try:
            url = f"https://www.alphavantage.co/query?function=CPI&interval=monthly&apikey={self.alpha_vantage_key}"
            response = self.requests.get(url)
            data = response.json()

            # Get latest two values to calculate year-over-year change
            latest_value = float(data['data'][-1]['value'])
            prev_year_value = float(data['data'][-13]['value'])  # 13 months ago

            # Calculate year-over-year change with sign
            yoy_change = ((latest_value - prev_year_value) / prev_year_value) * 100
            sign = '+' if yoy_change >= 0 else ''  # Add plus sign for positive values

            return {
                'index_value': latest_value,
                'yoy_change': yoy_change,
                'formatted_change': f"{sign}{yoy_change:.1f}",
                'note': 'CPI index (1982-1984=100) and YoY change'
            }
        except Exception as e:
            print(f"Error getting CPI data: {e}")
            return None

    def _get_treasury_yield_data(self):
        try:
            url = f"https://www.alphavantage.co/query?function=TREASURY_YIELD&interval=monthly&maturity=10year&apikey={self.alpha_vantage_key}"
            response = self.requests.get(url)
            data = response.json()
            return data
        except Exception as e:
            print(f"Error getting Treasury Yield data: {e}")
            return None

    def _get_feed_value(self, feed_name):
        """Get latest value from Adafruit IO feed"""
        try:
            feed = self.io.get_feed(feed_name)
            data = self.io.receive_data(feed['key'])
            return data['value'] if data else None
        except Exception as e:
            print(f"Error getting feed {feed_name}: {e}")
            return None

    def _get_financial_data(self, symbol, type_="stock"):
        """Get financial data from Alpha Vantage"""
        if not self.alpha_vantage_key:
            return None

        if type_ == "crypto":
            endpoint = "CURRENCY_EXCHANGE_RATE"
            params = f"from_currency={symbol}&to_currency=USD"
        else:
            endpoint = "GLOBAL_QUOTE"
            params = f"symbol={symbol}&entitlement=delayed"

        url = f"https://www.alphavantage.co/query?function={endpoint}&{params}&apikey={self.alpha_vantage_key}"
        response = self.requests.get(url)
        return response.json()

    def _get_stock_data(self, symbol):
        return self._get_financial_data(symbol, "stock")

    def _get_etf_data(self, symbol):
        return self._get_financial_data(symbol, "etf")

    def _get_crypto_data(self, symbol):
        try:
            data = self._get_financial_data(symbol, "crypto")

            if not data or "Realtime Currency Exchange Rate" not in data:
                return None

            exchange_rate = data["Realtime Currency Exchange Rate"]
            current_price = float(exchange_rate['5. Exchange Rate'])

            # Get the previous price for this symbol
            prev_price = self.crypto_price_history.get(symbol, current_price)

            # Calculate percent change
            if prev_price != 0:
                price_change = ((current_price - prev_price) / prev_price) * 100
            else:
                price_change = 0.0

            # Store current price for next comparison
            self.crypto_price_history[symbol] = current_price

            # Add change percentage to the response
            exchange_rate['change_percent'] = price_change

            return data

        except Exception as e:
            print(f"Error getting crypto data: {e}")
            return None


class IntegratedDisplay(EnhancedScrollingDisplay):
    def __init__(self, data_handler):
        super().__init__()
        self.data_handler = data_handler
        self.text_areas = []

    def clear_display(self):
        """Reset display state thoroughly"""
        # Clear all existing text areas
        for text_area in self.text_areas:
            self.matrix.set_text("", text_area)
        self.text_areas = []  # Reset text areas list

        self.matrix.set_text("")  # Clear main text
        self.scroll_text = ""
        time.sleep(0.1)  # Short delay to ensure clearing

    def update_display(self, data, mode):
        """Update display with new data"""
        try:
            if mode in ["Stock", "ETF"]:
                quote = data["Global Quote - DATA DELAYED BY 15 MINUTES"]
                symbol = quote['01. symbol']
                price = f"{float(quote['05. price']):.2f}"
                change = quote['10. change percent']
                self.show_financial(symbol, price, change)

            elif mode == "Crypto":
                exchange_rate = data["Realtime Currency Exchange Rate"]
                symbol = exchange_rate['1. From_Currency Code']
                price = f"{float(exchange_rate['5. Exchange Rate']):.2f}"
                change = f""
                self.show_financial(symbol, price, change)

            elif mode == "EconomicIndicators":
                self.show_economic_indicators(data)

        except Exception as e:
            print(f"Error updating display: {e}")
            self.matrix.set_text("Display Error")

    def handle_buttons(self):
        """Enhanced button handling with API integration"""
        change_detected = False

        # Handle UP button
        if self.is_button_pressed(self.button_up, 'up'):
            print("UP button pressed (debounced)")
            self.clear_display()  # Clear everything
            self.current_mode = (self.current_mode + 1) % len(self.modes)
            self.matrix.set_text(self.modes[self.current_mode])
            change_detected = True

        # Handle DOWN button (refresh)
        if self.is_button_pressed(self.button_down, 'down'):
            print("DOWN button pressed (debounced) - Refreshing data...")
            self.clear_display()  # Add explicit clear here
            self.matrix.set_text("Refreshing...")
            current_mode = self.modes[self.current_mode]

            # Request new data
            new_data = self.data_handler.update_data(current_mode)
            if new_data:
                self.update_display(new_data, current_mode)
            else:
                self.matrix.set_text("Error")
                time.sleep(1)

            change_detected = True

        return change_detected

    def show_economic_indicators(self, data):
        try:
            print("Starting economic indicators display")
            print(f"Number of text areas before clear: {len(self.text_areas)}")
            # First, thoroughly clear the display
            self.clear_display()  # Use the enhanced clear_display method
            print(f"Number of text areas after clear: {len(self.text_areas)}")

            # Create new text areas
            gdp_label = self.matrix.add_text(
                text_font=terminalio.FONT,
                text_position=(2, 6),
                text_scale=1,
                text_color=COLORS['RED']
            )

            cpi_label = self.matrix.add_text(
                text_font=terminalio.FONT,
                text_position=(2, 15),
                text_scale=1,
                text_color=COLORS['WHITE']
            )

            yld_label = self.matrix.add_text(
                text_font=terminalio.FONT,
                text_position=(2, 24),
                text_scale=1,
                text_color=COLORS['BLUE']
            )

            # Store labels for potential cleanup later
            self.text_areas = [gdp_label, cpi_label, yld_label]

            # Format and display data - each on its own label
            gdp_data = data['real_gdp']
            gdp_t = gdp_data['modern_value'] / 1000
            self.matrix.set_text(f"GDP:{gdp_t:.1f}T", gdp_label)

            cpi_data = data['cpi']
            self.matrix.set_text(f"CPI:{cpi_data['formatted_change']}%", cpi_label)

            yld_value = float(data['treasury_yield']['data'][-1]['value'])
            self.matrix.set_text(f"10Y:{yld_value:.2f}%", yld_label)

            print(f"Number of text areas after creation: {len(self.text_areas)}")
        except Exception as e:
            print(f"Unexpected error in show_economic_indicators: {str(e)}")
            self.matrix.set_text("Error")


def main():
    # Check for required keys
    required_keys = ['ALPHA_VANTAGE_KEY',
                     'ADAFRUIT_IO_USERNAME', 'ADAFRUIT_IO_KEY']

    if not all(os.getenv(key) for key in required_keys):
        print("ERROR: Missing required keys in settings.toml")
        return

    # Initialize systems
    data_handler = DataHandler()
    display = IntegratedDisplay(data_handler)

    print("Starting integrated display system...")

    # Initial data fetch
    for mode in ["Stock", "ETF", "Crypto", "EconomicIndicators"]:
        data_handler.update_data(mode)

    while True:
        # Handle button presses and display updates
        change_detected = display.handle_buttons()

        if change_detected:
            current_mode = display.modes[display.current_mode]
            stored_data = data_handler.data_store[current_mode]['data']

            if stored_data:
                display.update_display(stored_data, current_mode)
            else:
                display.matrix.set_text("No Data")

        # Update scroll position
        if display.current_mode != 3:  # Don't scroll for EconomicIndicators
            display.update_scroll()

        # Small delay
        time.sleep(0.01)

if __name__ == "__main__":
    main()