import os
import time
from datetime import datetime
from pathlib import Path
from typing import Any, Dict, Union
from urllib import parse

import pandas as pd

DATAFRAME_METADATA = "dataframe_metadata"
SQL_METADATA = "sql_metadata"
FILE_METADATA = "file_metadata"


def get_file_metadata(path: Union[str, Path]) -> Dict[str, Any]:
    """Gives metadata from loading a file.

    Note: we reserve the right to change this schema. So if you're using this come
    chat so that we can make sure we don't break your code.

    This includes:
    - the file size
    - the file path
    - the last modified time
    - the current time
    """
    if isinstance(path, Path):
        path = str(path)
    parsed = parse.urlparse(path)
    size = None
    scheme = parsed.scheme
    last_modified = time.time()
    timestamp = datetime.now().utcnow().timestamp()
    notes = f"File metadata is unsupported for scheme: {scheme} or path: {path} does not exist."

    if parsed.scheme == "" and os.path.exists(path):
        size = os.path.getsize(path)
        last_modified = os.path.getmtime(path)
        notes = ""

    return {
        FILE_METADATA: {
            "size": size,
            "path": path,
            "last_modified": last_modified,
            "timestamp": timestamp,
            "scheme": scheme,
            "notes": notes,
        }
    }


def get_dataframe_metadata(df: pd.DataFrame) -> Dict[str, Any]:
    """Gives metadata from loading a dataframe.

    Note: we reserve the right to change this schema. So if you're using this come
    chat so that we can make sure we don't break your code.

    This includes:
    - the number of rows
    - the number of columns
    - the column names
    - the data types
    """
    metadata = {}
    try:
        metadata["rows"] = len(df)
    except TypeError:
        metadata["rows"] = None

    try:
        metadata["columns"] = len(df.columns)
    except (AttributeError, TypeError):
        metadata["columns"] = None

    try:
        metadata["column_names"] = list(df.columns)
    except (AttributeError, TypeError):
        metadata["column_names"] = None

    try:
        metadata["datatypes"] = [str(t) for t in list(df.dtypes)]
    except (AttributeError, TypeError):
        metadata["datatypes"] = None
    return {DATAFRAME_METADATA: metadata}


def get_file_and_dataframe_metadata(path: str, df: pd.DataFrame) -> Dict[str, Any]:
    """Gives metadata from loading a file and a dataframe.

    Note: we reserve the right to change this schema. So if you're using this come
    chat so that we can make sure we don't break your code.

    This includes:
        file_meta:
            - the file size
            - the file path
            - the last modified time
            - the current time
        dataframe_meta:
        - the number of rows
        - the number of columns
        - the column names
        - the data types
    """
    return {**get_file_metadata(path), **get_dataframe_metadata(df)}


def get_sql_metadata(query_or_table: str, results: Union[int, pd.DataFrame]) -> Dict[str, Any]:
    """Gives metadata from reading a SQL table or writing to SQL db.

    Note: we reserve the right to change this schema. So if you're using this come
    chat so that we can make sure we don't break your code.

    This includes:
    - the number of rows read, added, or to add.
    - the sql query (e.g., "SELECT foo FROM bar")
    - the table name (e.g., "bar")
    - the current time
    """
    query = query_or_table if "SELECT" in query_or_table else None
    table_name = query_or_table if "SELECT" not in query_or_table else None
    if isinstance(results, int):
        rows = results
    elif isinstance(results, pd.DataFrame):
        rows = len(results)
    else:
        rows = None
    return {
        SQL_METADATA: {
            "rows": rows,
            "query": query,
            "table_name": table_name,
            "timestamp": datetime.now().utcnow().timestamp(),
        }
    }
