import os
import pandas as pd
import re
from shapely.geometry import LineString

from helpers import dataframe_converter


categories =['healthcare', 'services', 'transport', 'outdoor', 'supplies', 'restaurant', 'culture', 'education', 'physical']


def get_coordinates(osm_ids):
    return [osm_id_to_geom.get(osm_id, None) for osm_id in osm_ids]

def create_linestring(coords):
    # filter out any None values just in case
    valid_coords = [coord for coord in coords if coord is not None]
    if len(valid_coords) >= 2:  # a LineString requires at least 2 points
        return LineString(valid_coords)
    else:
        return None  # return None if not enough points to form a LineString

def get_centroid(linestring):
    if linestring and not linestring.is_empty:  # check if linestring is valid
        return linestring.centroid  # returns the centroid as a Point
    else:
        return None  # return None if the LineString is invalid or empty

def extract_outer_refs(members):
    return [member['ref'] for member in members if member.get('role') == 'outer']

def get_nodes_from_refs(outer_refs):
    # retrieve the nodes for each ref and flatten into one list
    nodes = []
    for ref in outer_refs:
        nodes.extend(osm_id_to_nodes.get(ref, []))  # extend the list with nodes (flattening)
    return nodes


if __name__ == "__main__":

    # full path to AGILE_2025_data_code folder
    fullpath = os.path.abspath(os.path.join(os.getcwd(), '..'))

    # input paths
    path_osm = os.path.join(fullpath, 'data', 'osm')
    path_tags = os.path.join(fullpath, 'data', 'tags')
    
    # output path
    path_pois = os.path.join(fullpath, 'results', 'pois')
    
    # create the output directory if it doesn't exist
    if not os.path.exists(path_pois):
        os.makedirs(path_pois)

    # read OSM data
    nodes = dataframe_converter(pd.read_csv(os.path.join(path_osm, 'nodes.csv')))
    ways = dataframe_converter(pd.read_csv(os.path.join(path_osm, 'ways.csv')))
    relations = dataframe_converter(pd.read_csv(os.path.join(path_osm, 'relations.csv')))

    # filter POIs per category from OSM data
    for category in categories:
        print(category)

        # read key-value tag pairs of category
        tags = pd.read_csv(os.path.join(path_tags, f'{category}.csv'))
        tags['value'] = tags['value'].fillna('.*')
        tags['string'] = tags.apply(lambda row: f"'{row['key']}': '{row['value']}'", axis=1)

        # pattern for filtering based on
        pattern = re.compile("|".join(tags['string'].values))

        # filter by key-value tag pairs
        filtered_nodes = nodes[nodes['tags'].astype(str).str.contains(pattern, regex=True, na=False)].copy()
        filtered_ways = ways[ways['tags'].astype(str).str.contains(pattern, regex=True, na=False)].copy()
        filtered_relations = relations[relations['tags'].astype(str).str.contains(pattern, regex=True, na=False)].copy()

        # lookup dictionaries for nodes: 'osm_id' -> 'geom', ways: 'osm_id' -> 'nodes'
        osm_id_to_geom = dict(zip(nodes['osm_id'], nodes['geom']))
        osm_id_to_nodes = dict(zip(ways['osm_id'], ways['nodes']))

        # transform filtered OSM ways to POI locations
        filtered_ways.loc[:, 'coordinates'] = filtered_ways['nodes'].apply(get_coordinates)
        filtered_ways.loc[:, 'linestring'] = filtered_ways['coordinates'].apply(create_linestring)
        filtered_ways.loc[:, 'geom'] = filtered_ways['linestring'].apply(get_centroid)
        filtered_ways.dropna(inplace=True)

        # transform filtered OSM relations to POI locations
        filtered_relations.loc[:, 'outer_refs'] = filtered_relations['members'].apply(extract_outer_refs)
        filtered_relations.loc[:, 'nodes'] = filtered_relations['outer_refs'].apply(get_nodes_from_refs)
        filtered_relations.loc[:, 'coordinates'] = filtered_relations['nodes'].apply(get_coordinates)
        filtered_relations.loc[:, 'linestring'] = filtered_relations['coordinates'].apply(create_linestring)
        filtered_relations.loc[:, 'geom'] = filtered_relations['linestring'].apply(get_centroid)
        filtered_relations.dropna(inplace=True)

        # combine filtered POIs to one dataframe
        filtered_nodes['type'] = 'node'
        filtered_ways['type'] = 'way'
        filtered_relations['type'] = 'relation'
        pois = pd.concat((filtered_nodes, filtered_ways[filtered_nodes.columns], filtered_relations[filtered_nodes.columns]))

        # save POIs per category
        pois.to_csv(os.path.join(path_pois, f'{category}.csv'), index=False)
    print('done')
