#!/usr/bin/env python3
import pprint
import re
import subprocess
from datetime import datetime, date, timedelta
from typing import List, Tuple

from espa_api_client.conf import LANDSAT_TILE_REGEX, GOOGLE_PREFIX
from espa_api_client.parse import search_landsat_tiles
from espa_api_client.OrderTemplate import OrderTemplate
from espa_api_client.Order import Order
from espa_api_client.Clients import Client


def _get_best_tile(tiles: List[str]) -> str:
    """
    Gets the most recent and highest available tier tile.
    :param tiles: list of available tiles
    :return: the highest tier and most recent tile
    """
    # sort in descending order by processing date
    tiles.sort(key=lambda x: x.split('_')[4], reverse=True)
    # the sort method is stable, so sorting by tier will produce the most recent higher tier tile ID
    tiles.sort(key=lambda x: x[-1])
    # the first tile in the list will be the best tile
    return tiles[0]


def _query_google(satellite: str, path: str, row: str, acquisition_date: str) -> List[str]:
    """
    Check with Google if the tile exists. If the tile does not exist, Google will return a command exception,
     which (I think) is then piped to stderr
    :param satellite: LANDSAT satellite
    :param path: tile's path
    :param row: tile's row
    :param acquisition_date: candidate acquisition date
    :return: list of available tiles
    """
    google_query = GOOGLE_PREFIX + satellite + "/01/" + path + "/" + row + "/" + \
                   satellite + "_*_" + path + row + "_" + acquisition_date + "_*_01_??/"

    print("Trying {}".format(google_query))
    query = subprocess.run(google_query, shell=True, stdout=subprocess.PIPE, universal_newlines=True)
    return search_landsat_tiles(query.stdout)


def _get_first_tile(start_date_obj: date, satellite: str, path: str, row: str) -> Tuple[str, int]:
    """
    Iterates 16 days from the start date and checks a tile exists.
    :param start_date_obj: the start date as a date object
    :param satellite: LANDSAT satellite
    :param path: tile's path
    :param row: tile's row
    :return: Tuple of the first tile within 16 days of the start date. The second element in the tuple is the distance
    from the first tile to the start date
    """
    print("Getting first tile")
    print("------------------------------")
    working_date_obj = start_date_obj
    for i in range(17):
        working_date = working_date_obj.strftime('%Y%m%d')
        result = _query_google(satellite, path, row, working_date)

        if result:
            return _get_best_tile(result), i
        working_date_obj += timedelta(days=1)
    return "", 0


def query_landsat_tilenames(
        start_date: date,
        end_date: date,
        satellite: str,
        wrs_tiles: List[Tuple[str, str]]) -> List[str]:
    """
    Retrieves a list of all available tiles of a LANDSAT satellite specified between the start and end date given a list
    of path-row pairs.
    :param start_date: the start date of the time frame
    :param end_date: end end date of the time frame
    :param satellite: LANDSAT satellite, valid options are strings ["LC08", "LE07"].
    :param wrs_tiles: a list of tuples of path and row as strings
    :return: list of all available tiles
    """
    missing_tiles = []
    for wrs_tile in wrs_tiles:
        path = wrs_tile[0]
        row = wrs_tile[1]

        first_tile = _get_first_tile(start_date, satellite, path, row)
        if first_tile:
            missing_tiles.append(first_tile[0])
            # print("First tile: {}".format(first_tile))
        else:
            # Limit the first tile search to 16 days. If the tile doesn't exist, skip to the next path-row pair
            print(
                "No tile exists within 16 days of {} for tile at ({}, {})".format(start_date, wrs_tile[0], wrs_tile[1]))
            continue
        # Add 16 days to the first tile to start the search from the second tile
        working_date = start_date + timedelta(days=(first_tile[1]+16))
        print("Getting next set of tiles")
        print("------------------------------")
        while working_date <= end_date:
            working_date_str = working_date.strftime('%Y%m%d')
            result = _query_google(satellite, path, row, working_date_str)

            if result:
                missing_tiles.append(_get_best_tile(result))
            else:
                print("WARNING: tile missing for {} {} {}".format(satellite, wrs_tile, working_date))
            working_date += timedelta(16)
    return missing_tiles


if __name__ == "__main__":
    wrs_tiles = [("015", "041"), ("015", "042"), ("016", "041"), ("016", "042")]
    start_date = datetime(2017, 4, 1)
    end_date = datetime(2017, 6, 26)
    l8_tiles = query_landsat_tilenames(start_date, end_date, "LC08", wrs_tiles)
    l7_tiles = query_landsat_tilenames(start_date, end_date, "LE07", wrs_tiles)
    # l7_tiles.remove('LE07_L1GT_016041_20170602_20170603_01_RT')
    # l7_tiles.remove('LE07_L1GT_016042_20170602_20170603_01_RT')
    # l7_tiles.remove('LE07_L1TP_016042_20170517_20170518_01_RT')
    # l8_tiles.remove('LC08_L1GT_016042_20170610_20170611_01_RT')
    pprint.pprint(l8_tiles)
    pprint.pprint(l7_tiles)

    template = OrderTemplate('example_sugar')
    order = Order(template, note="Other Google query test")
    client = Client()

    order.add_tiles("olitirs8_collection", l8_tiles)
    order.add_tiles("etm7_collection", l7_tiles)
    response = order.submit(client)

    print(response)
    orderid = response['orderid']
    downloads = client.download_order_gen(order_id=orderid)
    for download in downloads:
        print(download)
