import pygame
import urllib.request
import json
import sys
import textwrap

# -------------------- Config --------------------
WIDTH, HEIGHT = 720, 520
FPS = 60
BG_COLOR = (18, 22, 30)
CARD_COLOR = (32, 38, 50)
ACCENT = (0, 180, 220)
TEXT_COLOR = (230, 230, 230)
SUBTEXT = (160, 170, 185)

API_URL = "https://wttr.in/{city}?format=j1"


# -------------------- Weather Client --------------------
class WeatherClient:
    def __init__(self, unit="c"):
        self.unit = unit  # 'c' or 'f'

    def fetch(self, city):
        try:
            url = API_URL.format(city=city.replace(" ", "%20"))
            req = urllib.request.Request(url, headers={"User-Agent": "PygameWeatherApp"})
            with urllib.request.urlopen(req, timeout=8) as resp:
                data = json.load(resp)

            current = data["current_condition"][0]
            forecast = data["weather"][:3]

            return {
                "city": data["nearest_area"][0]["areaName"][0]["value"],
                "temp_c": current["temp_C"],
                "temp_f": current["temp_F"],
                "feels_c": current["FeelsLikeC"],
                "feels_f": current["FeelsLikeF"],
                "humidity": current["humidity"],
                "wind_kph": current["windspeedKmph"],
                "desc": current["weatherDesc"][0]["value"],
                "forecast": forecast,
            }
        except Exception as e:
            return {"error": str(e)}


# -------------------- UI Widgets --------------------
class TextBox:
    def __init__(self, x, y, w, h, font):
        self.rect = pygame.Rect(x, y, w, h)
        self.font = font
        self.text = ""
        self.active = False

    def draw(self, surf):
        color = ACCENT if self.active else SUBTEXT
        pygame.draw.rect(surf, CARD_COLOR, self.rect)
        pygame.draw.rect(surf, color, self.rect, 2)
        txt = self.font.render(self.text, True, TEXT_COLOR)
        surf.blit(txt, (self.rect.x + 8, self.rect.y + 6))

    def handle_event(self, event):
        if event.type == pygame.MOUSEBUTTONDOWN:
            self.active = self.rect.collidepoint(event.pos)
        if event.type == pygame.KEYDOWN and self.active:
            if event.key == pygame.K_BACKSPACE:
                self.text = self.text[:-1]
            elif event.unicode.isprintable():
                self.text += event.unicode


class Button:
    def __init__(self, x, y, w, h, text, font, callback):
        self.rect = pygame.Rect(x, y, w, h)
        self.text = text
        self.font = font
        self.callback = callback
        self.hover = False

    def draw(self, surf):
        col = (0, 210, 255) if self.hover else ACCENT
        pygame.draw.rect(surf, col, self.rect, border_radius=6)
        txt = self.font.render(self.text, True, (0, 0, 0))
        surf.blit(txt, txt.get_rect(center=self.rect.center))

    def handle_event(self, event):
        if event.type == pygame.MOUSEMOTION:
            self.hover = self.rect.collidepoint(event.pos)
        if event.type == pygame.MOUSEBUTTONDOWN and self.rect.collidepoint(event.pos):
            self.callback()


# -------------------- Main App --------------------
class WeatherApp:
    def __init__(self):
        pygame.init()
        self.screen = pygame.display.set_mode((WIDTH, HEIGHT))
        pygame.display.set_caption("Pygame Weather App")
        self.clock = pygame.time.Clock()
        self.font = pygame.font.SysFont("arial", 20)
        self.big = pygame.font.SysFont("arial", 26, bold=True)

        self.client = WeatherClient()
        self.data = None
        self.error = ""

        self.input = TextBox(30, 25, 320, 36, self.font)
        self.search_btn = Button(
            370, 25, 110, 36, "Search", self.font, self.search
        )
        self.unit_btn = Button(
            500, 25, 100, 36, "°C / °F", self.font, self.toggle_unit
        )

        if len(sys.argv) > 1:
            self.search(sys.argv[1])

    def toggle_unit(self):
        self.client.unit = "f" if self.client.unit == "c" else "c"

    def search(self, city=None):
        city = city or self.input.text.strip()
        if not city:
            return
        self.error = ""
        self.data = self.client.fetch(city)
        if "error" in self.data:
            self.error = self.data["error"]
            self.data = None

    def draw_weather(self):
        if not self.data:
            return

        x, y = 30, 90
        city = self.big.render(self.data["city"], True, ACCENT)
        self.screen.blit(city, (x, y))

        temp = self.data[f"temp_{self.client.unit}"]
        feels = self.data[f"feels_{self.client.unit}"]
        unit = "°C" if self.client.unit == "c" else "°F"

        info = [
            f"{self.data['desc']}",
            f"Temp: {temp}{unit} (Feels {feels}{unit})",
            f"Humidity: {self.data['humidity']}%",
            f"Wind: {self.data['wind_kph']} km/h",
        ]

        for i, line in enumerate(info):
            txt = self.font.render(line, True, TEXT_COLOR)
            self.screen.blit(txt, (x, y + 40 + i * 26))

        # Forecast
        fx, fy = 30, y + 150
        title = self.font.render("3-Day Forecast:", True, ACCENT)
        self.screen.blit(title, (fx, fy))

        for i, day in enumerate(self.data["forecast"]):
            dx = fx + i * 220
            dy = fy + 30
            max_t = day[f"maxtemp{self.client.unit.upper()}"]
            min_t = day[f"mintemp{self.client.unit.upper()}"]
            desc = day["hourly"][4]["weatherDesc"][0]["value"]

            block = [
                day["date"],
                f"{desc}",
                f"{max_t}{unit} / {min_t}{unit}",
            ]
            pygame.draw.rect(
                self.screen, CARD_COLOR, (dx, dy, 200, 80), border_radius=8
            )
            for j, line in enumerate(block):
                txt = self.font.render(line, True, SUBTEXT)
                self.screen.blit(txt, (dx + 10, dy + 10 + j * 24))

    def run(self):
        while True:
            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    pygame.quit()
                    sys.exit()
                self.input.handle_event(event)
                self.search_btn.handle_event(event)
                self.unit_btn.handle_event(event)
                if event.type == pygame.KEYDOWN and event.key == pygame.K_RETURN:
                    self.search()

            self.screen.fill(BG_COLOR)
            self.input.draw(self.screen)
            self.search_btn.draw(self.screen)
            self.unit_btn.draw(self.screen)

            if self.error:
                err = self.font.render(f"Error: {self.error}", True, (255, 80, 80))
                self.screen.blit(err, (30, 75))
            self.draw_weather()

            pygame.display.flip()
            self.clock.tick(FPS)


if __name__ == "__main__":
    WeatherApp().run()