"""SQLite waypoint files used in Potensic Atom 1 & 2."""

import os
import logging
import sqlite3
from typing import Iterable
from itertools import islice

import geojson

log = logging.getLogger(__name__)

# I couldn't get things to work when I generate the sqlite file directly from
# python initially. I had to modify the values in an existing generated sqlite
# file from potensic pro (when they were basically identical)
#
# I carried out lots of testing and determined:
# - Only flightrecordbean and multipointbean tables matter.
#   All other tables are irrelevant (including generated flightnotes record).
# - The info in flighrecordbean is purely information.
#   It doesn't affect anything in the flight or restrict anything.
# - The point limit per mission is advertised as 30, but it's actually about 45 points
#   per flight in PotensicPro. Perhaps this could be modded in the app, but we
#   have to work with it for now (smaller flightplans, or simplified).
# - Coordinate rounding level in multipointbean doesn't seem to affect anything.
# - I ran the sqldiff for sqlite and determined no significant differences between
#   the files. As far as I can tell they should be cross compatible. I also verified
#   it's not some weird dos/unix bit difference (dos2unix). There is something else
#   preventing the compatibility of the python generated sqlite file, this wasn't it.
#
# Finally, I determined it was PRAGMA values for user_version=5 and schema_version=22,
# that have to match the files generated by PotensicPro. Presumably some basic form of
# schema migration is done by the app & this is a safeguard.


def create_tables(conn):
    cursor = conn.cursor()

    # Core system tables
    # NOTE these tables are present, but we aren't sure if they are used for anything,
    # so they are added just in case
    cursor.execute("CREATE TABLE IF NOT EXISTS android_metadata (locale TEXT)")
    cursor.execute(
        "CREATE TABLE IF NOT EXISTS table_schema (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT, type INTEGER)"
    )
    cursor.execute(
        "CREATE TABLE IF NOT EXISTS flightnotes (id INTEGER PRIMARY KEY AUTOINCREMENT, null_lpcolumn INTEGER, distance REAL, duration INTEGER, height REAL, speed REAL, starttime INTEGER)"
    )
    cursor.execute(
        "CREATE TABLE IF NOT EXISTS flightlog (id INTEGER PRIMARY KEY AUTOINCREMENT, null_lpcolumn INTEGER, isupload INTEGER, length INTEGER, name TEXT)"
    )
    cursor.execute(
        "CREATE TABLE IF NOT EXISTS uomuploadbody (id INTEGER PRIMARY KEY AUTOINCREMENT, altitude INTEGER, course INTEGER, flightenumname TEXT, flightsorties INTEGER, flightstatusenumname TEXT, gs INTEGER, height INTEGER, latitude INTEGER, longitude INTEGER, sn TEXT, timemillis INTEGER, vs INTEGER)"
    )
    cursor.execute(
        "CREATE TABLE IF NOT EXISTS uomrecord (id INTEGER PRIMARY KEY AUTOINCREMENT, sorties INTEGER, uomstatechangedtime INTEGER, uomstateenumname TEXT)"
    )

    # Insert default metadata
    cursor.execute("INSERT INTO android_metadata (locale) VALUES (?)", ("en_GB",))

    # Insert schema entries
    table_schema_entries = [
        (1, "flightrecordbean", 0),
        (2, "multipointbean", 0),
        (3, "flightnotes", 0),
        (4, "flightlog", 0),
        (5, "uomuploadbody", 0),
        (6, "uomrecord", 0),
    ]
    cursor.executemany(
        "INSERT INTO table_schema (id, name, type) VALUES (?, ?, ?)",
        table_schema_entries,
    )

    # Tables for waypoints
    # NOTE these two tables are key
    # - flightrecordbean has one entry per waypoint flight, however associated
    #   metadata appears to be purely information (not doing anything)
    # - multipointbean contains the coordinates of waypoints to fly to
    cursor.execute("""
        CREATE TABLE IF NOT EXISTS flightrecordbean (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            date TEXT,
            duration INTEGER,
            height TEXT,
            mileage TEXT,
            num INTEGER,
            speed TEXT
        )
    """)
    cursor.execute("""
        CREATE TABLE IF NOT EXISTS multipointbean (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            flightrecordbean_id INTEGER,
            lat REAL,
            lng REAL
        )
    """)

    # Set the user_version & schema_version (essential)
    # NOTE I assume PotensicPro uses schema_version as a check, so
    # without this, loading the file doesn't work!
    cursor.execute("PRAGMA user_version = 5;")
    cursor.execute("PRAGMA schema_version = 22;")


def chunked(iterable: Iterable, size: int):
    """Yield successive `size`-sized chunks from iterable."""
    it = iter(iterable)
    while chunk := list(islice(it, size)):
        yield chunk


def generate_potensic_sqlite(
    featcol: geojson.FeatureCollection, db_path: str = "map.db"
):
    """
    Generate SQLite DB in `db_path` with one flight record and many multipointbean entries.

    Args:
        waypoints (list): List of (lat, lon) tuples.
        db_path (str): Path to the SQLite file to create.
    """
    all_features = featcol.get("features", [])
    if not all_features:
        raise ValueError("No features found in feature collection")

    # Handle altitude & speed params (information only), else defaults
    first_geom = all_features[0]
    altitude = round(first_geom.get("properties").get("altitude", 110))
    speed = round(first_geom.get("properties").get("speed", 8))

    # NOTE PotensicPro only supports ~45 waypoints per flightplan,
    # so get get them all here, then split up further down
    all_waypoints: list[tuple[float, float]] = [
        feat.get("geometry").get("coordinates")[0:2] for feat in all_features
    ]

    if os.path.exists(db_path):
        log.info(f"Deleting existing db at {db_path}")
        os.remove(db_path)

    conn = sqlite3.connect(db_path)
    create_tables(conn)
    cursor = conn.cursor()

    log.debug("Creating Potensic SQLite metadata")
    # NOTE instead of an actual timestamp for the `date` field, we name the
    # flights in order (this is what is displayed to the user in the sidebar)
    # date_str = datetime.now(tz=timezone.utc).strftime("%-d,%-m,%Y")

    flight_id = 1
    waypoint_id = 1
    start_idx = 1
    # NOTE here we split into 10 waypoint chunks, to allow for easy resumption
    # of the flight if it stops. The PotensicPro app is a bit flaky with this,
    # and sometimes the flight may stop unexpectedly
    for chunk in chunked(all_waypoints, 10):
        end_idx = start_idx + len(chunk) - 1  # calculate last index in this chunk

        duration = len(chunk) * 5 * 1000  # 5000ms per point
        mileage = len(chunk) * 10  # 10m per point

        # Insert new flight record into flightrecordbean
        cursor.execute(
            """
            INSERT INTO flightrecordbean(
              id,
              date,
              duration,
              height,
              mileage,
              num,
              speed
            )
            VALUES (?, ?, ?, ?, ?, ?, ?)
        """,
            (
                flight_id,
                f"{start_idx}-{end_idx}",
                duration,
                f"{altitude}m",
                f"{mileage}m",
                len(chunk),
                f"{speed}km/h",
            ),
        )

        # Insert waypoints in multipointbean
        # NOTE the waypoints are geojson lon,lat format
        log.debug("Inserting Potensic SQLite waypoint data")
        for lon, lat in chunk:
            cursor.execute(
                """
                INSERT INTO multipointbean(id, flightrecordbean_id, lat, lng)
                VALUES (?, ?, ?, ?)
            """,
                (waypoint_id, flight_id, lat, lon),
            )
            waypoint_id += 1

        start_idx = end_idx + 1  # next chunk starts after this one ends
        flight_id += 1

    conn.commit()
    conn.close()
    log.info(
        f"Database created at ({db_path}) with ({waypoint_id - 1}) "
        f"waypoints across ({flight_id - 1}) flights."
    )
    return db_path


if __name__ == "__main__":
    sample_coords = [
        (-0.13512505592254342, 51.565597097346455),
        (-0.1351416738206126, 51.565631231208755),
        (-0.13507375720794812, 51.565631231208755),
        (-0.13501523329526322, 51.56561281688707),
        (-0.13503835387621166, 51.56557913214192),
        (-0.1350708671484142, 51.56560158864778),
    ]
    generate_potensic_sqlite(sample_coords, "map.db")
