import os
from datetime import datetime
from typing import Optional, List
from abc import abstractmethod
import csv
import psycopg2
import quandl
import numpy as np
import pandas as pd

import alpaca_trade_api as tradeapi

from wsbtrading.instrumentation import Alpaca as iAlpaca
from wsbtrading.instrumentation import Quandl as iQuandl


class CSV:
    @abstractmethod
    def writerow(self, row: List[str]) -> None:
        pass

    @abstractmethod
    def writerows(self, rows: List[List[str]]) -> None:
        pass

    @abstractmethod
    def dialect(self) -> csv.Dialect:
        pass


def quandl_api():
    """Creates the initial connection to Quandl API.

    Args:
        subscription: denotes subscription API to connect to

    Returns:
        an API connection to Alpaca

    **Example**

    .. code-block:: python

        import quandl
        from wsbtrading import data_io

        data_io.quandl_api()
        quandl.get_table('SHARADAR/SEP', date='2013-03-15', ticker='ZZ')
    """
    quandl.ApiConfig.api_key = iQuandl.api_key


def alpaca_rest_api_conn(trading_type: str):
    """Creates the initial connection to Alpaca API.

    Args:
        trading_type: denotes live versus paper trading

    Returns:
        an API connection to Alpaca

    **Example**

    .. code-block:: python

        from wsbtrading import data_io
        alpaca_api = data_io.alpaca_rest_api_conn(trading_type='paper_trading')
    """
    api_key = iAlpaca.api_key
    secret_key = iAlpaca.secret_key
    base_url = iAlpaca.api_call[trading_type]['base_url']

    return tradeapi.REST(api_key, secret_key, base_url=base_url)


def postgres_conn(database: str = 'wsbtrading',
            user: str = 'postgres',
            password: str = 'postgres',
            host: str = '127.0.0.1',
            port: str = '5432'):
    """Creates the initial connection to Postgres.

    Args:
        database: denotes live versus paper trading
        user: the user to sign in as
        password: the password to sign in with
        host: the host URL
        port: the port number

    Returns:
        a Postgres connection

    **Example**

    .. code-block:: python

        from wsbtrading import data_io
        postgres_conn = data_io.postgres_conn()
    """
    conn = psycopg2.connect(database=database, user=user, password=password, host=host, port=port)
    cur = conn.cursor()

    return conn, cur


def generate_path_to_write(environment: str,
                           granularity: str,
                           dataset_name: str,
                           root_path: str = '/group/wsbtrading',
                           timestamp: Optional[str] = None):
    """Generates an HDFS path from the inputs.
    The output path is of the form ``/ROOT_PATH/ENVIRONMENT/GRANULARITY/DATASET_NAME/TIMESTAMP``

    Args:
        environment: possible values are 'dev' or 'prod'
        granularity: possible values are 'stream', 'minute', 'daily', 'weekly', 'monthly'
        dataset_name: the name of the dataset, with no spaces e.g. stock_ticker_metadata
        root_path: the root for your dataset path, e.g. ``/user/your_user`` or ``/group/wsbtrading``.
        Defaults to ``/group/wsbtrading``.
        experiment_id: the unique identifer for the experiment
        timestamp: the timestamp in UTC when the job ran, autogenerated. Can be overridden e.g. if user wants to
                   pass in some id like 'v1' or 'run10' or 'run_with_fixes'.

    Returns:
        The path to be written to

    **Example**
    .. code-block:: python

        from phobos import data_io
        data_io.generate_path_to_write(
            environment='prod',
            granularity='daily',
            dataset_name='stock_ticker_metadata'
        )
        # /group/wsbtrading/prod/aggregate/stock_ticker_metadata/file1.parquet...file2.parquet
    """
    supported_environments = ['dev', 'prod']
    supported_granularities = ['stream', 'minute', 'daily', 'weekly', 'monthly']

    if environment not in supported_environments or granularity not in supported_granularities:
        raise ValueError('Unsupported environment or granularity.')

    dataset_name = dataset_name.replace(' ', '_').lower()
    timestamp = timestamp or datetime.utcnow().strftime('%Y-%m-%d_%H-%M-%S')

    return os.path.join(root_path, environment, granularity, dataset_name, timestamp)


def _get_table_field_names(table_name: str) -> List[str]:
    """Queries the field names of a given table.

    Args:
        table_name: the name of the table to query

    Returns:
        a list of column names

    **Example**

    .. code-block:: python

        from phobos import data_io
        column_list = data_io._get_table_field_names(table_name='company_listing_status')
    """
    try:
        cur.execute(f'Select * FROM {table_name} LIMIT 0')
        col_names = [desc[0] for desc in cur.description]
    except psycopg2.Error as e:
        print(f'error: {e}')

    return col_names


def insert_csv_to_sql(table_name: str, csv_path: str, delimiter: str = ','):
    """Given a SQL table.

    Args:
        table_name: the name of the table to query
        csv_path: the path to the data file to import
        delimiter: the type of delimiter for the file

    **Example**

    .. code-block:: python

        from phobos import data_io
        data_io.insert_csv_to_sql(table_name='company_listing_status', csv_path='../file.csv')
    """
    col_names = _get_table_field_names(table_name=table_name)

    try:
        df = pd.read_csv(csv_path, index_col=0)
        df.replace({np.nan: None})
        df.to_csv('clean_csv.csv')
        csv_contents = open('clean_csv.csv', 'r')
        next(csv_contents)
    except psycopg2.Error as e:
        print(f'error: {e}')

    try:
        cur.copy_from(file=csv_contents,
                      table=table_name,
                      null=None,
                      columns=col_names,
                      sep=delimiter)
        conn.commit()
        cur.close()
        conn.close()
        print("Success!")
    except psycopg2.Error as e:
        print(f'error: {e}')






# https://docs.databricks.com/spark/latest/spark-sql/spark-pandas.html
# Enable Arrow-based columnar data transfers
# spark.conf.set("spark.sql.execution.arrow.pyspark.enabled", "true")
#
# # Generate a pandas DataFrame
# pdf = pd.DataFrame(np.random.rand(100, 3))
#
# # Create a Spark DataFrame from a pandas DataFrame using Arrow
# df = spark.createDataFrame(pdf)
#
# # Convert the Spark DataFrame back to a pandas DataFrame using Arrow
# result_pdf = df.select("*").toPandas()

# import config
# import websocket
# import json
# import requests

# def on_open(ws):
#     print("opened")
#     auth_data = {
#         "action": "authenticate",
#         "data": {"key_id": config.API_KEY, "secret_key": config.SECRET_KEY}
#     }
#
#     ws.send(json.dumps(auth_data))
#
#     listen_message = {"action": "listen", "data": {"streams": ["AM.TSLA"]}}
#
#     ws.send(json.dumps(listen_message))
#
#
# def on_message(ws, message):
#     print("received a message")
#     print(message)
#
#
# def on_close(ws):
#     print("closed connection")
#
#
# socket = "wss://data.alpaca.markets/stream"
#
#
# ws = websocket.WebSocketApp(socket, on_open=on_open, on_message=on_message, on_close=on_close)
# ws.run_forever()
