#!/usr/bin/env python3

# Install:
#   pip install geopandas pandas

from pathlib import Path

import geopandas as gpd
import pandas as pd


# =========================
# CONFIG
# =========================

BASE_DIR = Path.cwd()
INPUT_DIR = BASE_DIR
OUTPUT_DIR = BASE_DIR

SHP_FILE = INPUT_DIR / "Human_activity_records_complete.shp"
METADATA_FILE = INPUT_DIR / "Meta_data_human_activity_data.csv"

OUTPUT_FILE = OUTPUT_DIR / "Human_activity_records_complete.csv"

JOIN_FIELDS = [
    "Category",
    "Source",
    "Description",
    "Format",
    "Period",
    "Citation",
]

# =========================


def main():

    OUTPUT_DIR.mkdir(exist_ok=True)

    print("Reading shapefile...")
    gdf = gpd.read_file(SHP_FILE)

    print("Reading metadata...")
    metadata = pd.read_csv(METADATA_FILE, encoding="latin1") # Accommodate older encoding

    # Check required fields
    if "meta_no" not in gdf.columns:
        raise ValueError("Shapefile does not contain a 'meta_no' field.")

    required_metadata = ["Meta No"] + JOIN_FIELDS
    missing = [field for field in required_metadata if field not in metadata.columns]

    if missing:
        raise ValueError(
            f"Metadata CSV is missing required fields: {', '.join(missing)}"
        )

    # Ensure join fields use compatible types
    gdf["meta_no"] = pd.to_numeric(gdf["meta_no"], errors="coerce").astype("Int64")
    metadata["Meta No"] = pd.to_numeric(
        metadata["Meta No"], errors="coerce"
    ).astype("Int64")

    # Metadata should contain only one record for each Meta No
    duplicates = metadata.loc[
        metadata["Meta No"].duplicated(keep=False),
        "Meta No"
    ].dropna().unique()

    if len(duplicates) > 0:
        raise ValueError(
            "Metadata CSV contains duplicate 'Meta No' values: "
            + ", ".join(map(str, duplicates))
        )

    # Select only requested metadata fields and convert names to lowercase
    metadata = metadata[["Meta No"] + JOIN_FIELDS].rename(
        columns={
            "Meta No": "meta_no",
            **{field: field.lower() for field in JOIN_FIELDS},
        }
    )

    print("Calculating latitude and longitude...")

    # Reproject a copy of the geometry to geographic coordinates
    geographic = gdf.to_crs("EPSG:4326")

    gdf["latitude"] = geographic.geometry.y
    gdf["longitude"] = geographic.geometry.x

    print("Joining metadata...")

    # Remove geometry for CSV
    df = pd.DataFrame(gdf.drop(columns="geometry"))

    # Many activity records can link to one metadata record
    df = df.merge(
        metadata,
        on="meta_no",
        how="left",
        validate="many_to_one",
    )

    print(f"Writing {len(df):,} records...")
    df.to_csv(
        OUTPUT_FILE,
        index=False,
        encoding="utf-8-sig",
    )

    matched = df["category"].notna().sum()
    unmatched = len(df) - matched

    print(f"Matched metadata:   {matched:,}")
    print(f"Unmatched records:  {unmatched:,}")
    print(f"Output: {OUTPUT_FILE}")


if __name__ == "__main__":
    main()