import csv
from os import write
from pathlib import Path

import right_tree.api.data
from .filters import *
from .wms_utils import get_address_from_coordinates, get_point_from_coordinates

import pdfkit
import pandas as pd
from PyPDF2 import PdfFileMerger


CSV_FILENAME = 'plants.csv'
PLANTING_GUIDE_PDF_FILENAME = 'planting_guide.pdf'
HEADER_FIELDS = ['Names', 'Growth Form / Max Height (m) / Spacing (m) / Forest Position', 'Moisture Preferences',
                 'Tolerances (Water / Drought / Frost / Salinity)', 'Ecosystem Services', 'Carbon Sequestration Rate', 'Planting Stage']


def get_plant_resource_filepath(filename=CSV_FILENAME, resource_dir='generated_resources'):
    """ Retrives the filepath for the plant csv file.
    """
    return Path(right_tree.api.data.__file__).resolve().parent / resource_dir / filename


def get_location_filters(request):
    """ Retrives the selected location data from the request.
    """
    filter_rows = [['LOCATION FILTERS:', ' ']]
    coordinates = request.query_params.get('coordinates')

    if coordinates is not None:
        eco_district_layer = ecological_district_coordinate_filter(
            coordinates).first()
        point = get_point_from_coordinates(coordinates)
        address = get_address_from_coordinates(coordinates)

        filter_rows.append(['Point coordinates:', point])
        filter_rows.append(['Ecological region:', eco_district_layer.ecologic_1 or '' ])
        filter_rows.append(['Ecological district:', eco_district_layer.ecologic_2 or ' '])
        filter_rows.append(['Property address:', address['full_address'] if address is not None else ' '])
    else:
        filter_rows.append(["None specified", " "])

    return filter_rows


def get_soil_filters(request):
    """ Retrives the selected soil type data from the request.
    """
    filter_rows = [['SOIL FILTERS:', ' ']]
    soil_variant = request.query_params.get('soilVariant')
    coordinates = request.query_params.get('coordinates')

    if soil_variant is not None and coordinates is not None:
        soil_order_obj = soil_order_coordinate_filter(coordinates).first()

        filter_rows.append(['Soil Order:', f"{soil_order_obj.name or ' '} ({soil_order_obj.code or ' '})"])
        filter_rows.append(['Soil Variant:', soil_variant])
    else:
        filter_rows.append(["None specified", " "])

    return filter_rows


def get_site_filters(request):
    """ Retrives the selected site data from the request.
    """
    filter_rows = [['SITE FILTERS:', ' ']]

    habitat = request.query_params.get('habitat')
    zone = request.query_params.get('zone')
    if zone is not None and habitat is not None:
        habitat_json = json.loads(habitat)
        zone_json = json.loads(zone)
        filter_rows.append(['Habitat:', habitat_json.get("name", " ")])
        filter_rows.append(['Zone Name:', zone_json.get("name", " ")])
        filter_rows.append(['Zone Variant:', zone_json.get("variant", " ")])
        filter_rows.append(['Zone Refined Variant:', zone_json.get("refined_variant", " ")])
    else:
        filter_rows.append(["None specified", " "])

    return filter_rows


def get_additional_region_info(request):
    """ If the location coordinates fall within the CHCH or Auckland regions then return a description of where to find more information.
    """
    coordinates = request.query_params.get('coordinates')
    if coordinates is not None:
        if is_in_christchurch(coordinates):
            return [["Your location falls within the ecosystem type covered by the Christchurch Council ecosystem maps - further information can be obtained from Ōtautahi/Christchurch ecosystems map link to: https://ccc.govt.nz/environment/land/ecosystem-map", " "], [' ', ' ']]
        elif is_in_auckland(coordinates):
            return [["Your location falls within the ecosystem type covered by the Auckland Council Tiaki Tāmaki Makaurau Conservation map - further information can be obtained from tiaki Tāmaki Makaurau conservation Auckland  - link to https://www.tiakitamakimakaurau.nz/conservation-map/", " "], [' ', ' ']]
    
    return []

def get_filter_values(request):
    """ Retrives all selected values/filters from the request.
    """
    filter_rows = []

    # Add all the location filters
    filter_rows += get_location_filters(request)
    filter_rows.append([' ', ' '])

    # Add the soil filters
    filter_rows += get_soil_filters(request)
    filter_rows.append([' ', ' '])

    # Add the project site filters
    filter_rows += get_site_filters(request)
    filter_rows.append([' ', ' '])
    
    filter_rows += get_additional_region_info(request)

    return filter_rows


def write_csv_filter_info(request, writer):
    """ Retrieves and writes filter information to a CSV file given a writer.
    """
    for filter_row in get_filter_values(request):
        writer.writerow(filter_row)


def write_csv_plant_info(plant_data, writer):
    """ Writes plant list information to a CSV file given a writer.
    """
    writer.writerow(HEADER_FIELDS)
    for plant in plant_data:
        plant_data_row = [
            plant.display_name,
            plant.display_growth_form,
            plant.moisture_preferences,
            plant.plant_tolerances,
            plant.ecosystem_services,
            plant.carbon_sequestration,
            plant.stage
        ]
        writer.writerow(plant_data_row)


def create_plant_csv_file(request, plant_data):
    """ Constructs a csv file that contains selected filter values and the resulting plant list.
    """
    with open(get_plant_resource_filepath(), 'w', encoding='UTF8') as f:
        writer = csv.writer(f)

        # Write filter/selected values
        write_csv_filter_info(request, writer)

        # Write the plant data
        write_csv_plant_info(plant_data, writer)


def generate_pdf(data_name, data, csv_generation_function):
    """ Generates a pdf from a csv given data and a csv generation method. 
        Requires an html file to be generated as an intermediate step.
    """

    # Define filepaths - some are required as intermediate files
    csv_filepath = get_plant_resource_filepath(f'{data_name}.csv')
    html_filepath = get_plant_resource_filepath(f'{data_name}.html')
    pdf_filepath = get_plant_resource_filepath(f'{data_name}.pdf')

    # Create an initial csv file with the data
    with open(csv_filepath, 'w', encoding='UTF8') as f:
        writer = csv.writer(f)
        csv_generation_function(data, writer)

    # Convert the csv to and html file then to a pdf
    CSV = pd.read_csv(csv_filepath)
    CSV.to_html(html_filepath)
    pdfkit.from_file(str(html_filepath), str(pdf_filepath))


def create_planting_guide_pdf(request, plant_data):
    """ Creates a planting guide pdf document with a pre-generated planting guide with
        filter and plant list tabular informtation appended.
    """

    # Define the names of the resource files as used in multiple places
    filter_file_prefix = "filters"
    plant_list_file_prefix = "plant_list"

    # Generate the filters and plant list pdf files
    generate_pdf(filter_file_prefix, request, write_csv_filter_info) # TODO: space values appear as NaN... this should be fixed
    generate_pdf(plant_list_file_prefix, plant_data, write_csv_plant_info)

    # Create a list of pdfs that need to be merged
    planting_guide_pdf_filepath = get_plant_resource_filepath(PLANTING_GUIDE_PDF_FILENAME, 'resources')
    filter_pdf_filepath = get_plant_resource_filepath(f'{filter_file_prefix}.pdf')
    plant_list_pdf_filepath = get_plant_resource_filepath(f'{plant_list_file_prefix}.pdf')
    pdfs = [planting_guide_pdf_filepath, filter_pdf_filepath, plant_list_pdf_filepath]

    # Merge pdfs
    merger = PdfFileMerger()
    for pdf in pdfs:
        merger.append(str(pdf))

    # Create the final output pdf with all merged documents
    output_pdf_filepath = get_plant_resource_filepath(PLANTING_GUIDE_PDF_FILENAME)
    merger.write(str(output_pdf_filepath))
    merger.close()
