"""Collect readings from an API into a local SQLite database."""
import argparse
import json
import logging
import os
import sqlite3
import time
from dataclasses import dataclass, astuple

import requests

DB_PATH = "readings.db"
API_URL = "https://api.example.com/v1/current"
log = logging.getLogger("tracker")


@dataclass(frozen=True)
class Reading:
    station_id: str
    station_name: str
    collected_at: str
    metric: str
    value: float


def to_records(payload):
    station = payload.get("station", {})
    when = payload.get("collected_at", "")
    return [{"station_id": station.get("id", "?"),
             "station_name": station.get("name", "unknown"),
             "collected_at": when,
             "metric": o.get("metric", "unknown"),
             "value": float(o.get("value", 0))}
            for o in payload.get("observations", [])]


def connect(path=DB_PATH):
    conn = sqlite3.connect(path)
    conn.execute("""
        CREATE TABLE IF NOT EXISTS readings (
            station_id TEXT NOT NULL, station_name TEXT NOT NULL,
            collected_at TEXT NOT NULL, metric TEXT NOT NULL, value REAL NOT NULL,
            UNIQUE (station_id, collected_at, metric))
    """)
    return conn


def store(conn, readings):
    before = conn.total_changes
    conn.executemany("INSERT OR IGNORE INTO readings VALUES (?, ?, ?, ?, ?)",
                     [astuple(r) for r in readings])
    conn.commit()
    return conn.total_changes - before


def fetch(url, attempts=3):
    for attempt in range(1, attempts + 1):
        try:
            response = requests.get(
                url, timeout=10,
                headers={"Authorization": f"Bearer {os.environ['WEATHER_TOKEN']}"})
            response.raise_for_status()
            return response.json()
        except requests.RequestException as err:
            log.warning("attempt %d/%d failed: %s", attempt, attempts, err)
            if attempt == attempts:
                raise
            time.sleep(2 ** attempt)


def report(conn):
    for name, n, avg, high in conn.execute("""
        SELECT station_name, COUNT(*), ROUND(AVG(value), 1), MAX(value)
        FROM readings WHERE metric = ? GROUP BY station_name ORDER BY AVG(value) DESC
    """, ("temperature",)):
        print(f"{name:10s} {n:3d} samples  avg {avg:5.1f}  peak {high:5.1f}")


def main():
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument("--once", action="store_true",
                        help="load sample_response.json instead of calling the API")
    parser.add_argument("--report", action="store_true", help="summarize what is stored")
    args = parser.parse_args()

    logging.basicConfig(level=logging.INFO,
                        format="%(asctime)s %(levelname)s %(message)s")
    conn = connect()

    if args.report:
        report(conn)
        return

    if args.once:
        with open("sample_response.json", encoding="utf-8") as f:
            payload = json.load(f)
    else:
        payload = fetch(API_URL)

    added = store(conn, [Reading(**row) for row in to_records(payload)])
    log.info("stored %d new reading(s) of %d received", added, len(to_records(payload)))


if __name__ == "__main__":
    main()
