import os
import json
import csv
from datetime import date, datetime, timedelta
from pprint import pprint

from pyfakefs.fake_filesystem_unittest import TestCase
import pytest
import numpy as np
import pandas as pd
from appdirs import user_cache_dir
from aynse import nse
import click
import warnings

h = nse.NSEHistory()

# Skip marker for tests that require external services (httpbin)
skip_httpbin = pytest.mark.skip(reason="httpbin tests are flaky in CI due to retry logic")


def get_data(symbol, from_date, to_date, series):
    params = {
            'symbol': symbol,
            'from': from_date.strftime('%d-%m-%Y'),
            'to': to_date.strftime('%d-%m-%Y'),
            'series': '["{}"]'.format(series),
    }
    
    return h._get("stock_history", params)

def test_cookie():
    r = h._get("equity_quote_page", params={})
    assert r.status_code == 200
    assert "nseappid" in r.cookies
    symbol = "RELIANCE"
    from_date = date(2019,1,1)
    to_date = date(2019,1,31)
    series = "EQ"
    d = get_data(symbol, from_date, to_date, series)
    try:
        j = json.loads(d.text)
    except json.JSONDecodeError:
        pytest.skip("NSE returned non-JSON response for stock history endpoint")
    assert 'data' in j
    if not j["data"]:
        pytest.skip("NSE returned empty stock history payload")
    assert "CH_TIMESTAMP" in j["data"][0]
    assert "CH_SYMBOL" in j["data"][0]


def test__get():
    symbol = "RELIANCE"
    from_date = date(2019,1,1)
    to_date = date(2019,1,31)
    series = "EQ"
    d = get_data(symbol, from_date, to_date, series)
    print(d.text)
    try:
        j = json.loads(d.text)
    except json.JSONDecodeError:
        pytest.skip("NSE returned non-JSON response for stock history endpoint")
    assert 'data' in j
    if not j["data"]:
        pytest.skip("NSE returned empty stock history payload")
    assert "CH_TIMESTAMP" in j["data"][0]
    assert "CH_SYMBOL" in j["data"][0]

@skip_httpbin
def test__get_http_bin():
    """Test HTTP GET with httpbin - skipped in CI due to retry logic incompatibility."""
    h = nse.NSEHistory()
    h.base_url = "https://httpbin.org"
    h.path_map['bin'] = '/get'
    
    params = {"p1":'1' , "p2": "a"}
    r = h._get("bin", params)
    _params = json.loads(r.text)['args']
    assert params == _params

def setup_test(self):
    """
    FakeFS creates a fake file systems and in process looses the CA Certs
    Which fails the test while running stocks
    To fix that CA certificates will be read and then placed back
    """
    import certifi
    self.path = certifi.where()
    with open(self.path) as fp:
        self.certs = fp.read()
    self.setUpPyfakefs()        
    ## Restoring the CA certs
    self.fs.create_file(self.path)
    with open(self.path, "w") as fp:
        fp.write(self.certs)
    # Use a predictable cache directory in the fake filesystem
    os.environ['J_CACHE_DIR'] = '/fakecache'



class TestNSECache(TestCase):
    def setUp(self):
        setup_test(self)
        """
        FakeFS creates a fake file systems and in process looses the CA Certs
        Which fails the test while running stocks
        To fix that CA certificates will be read and then placed back
        import certifi
        self.path = certifi.where()
        with open(self.path) as fp:
            self.certs = fp.read()
        self.setUpPyfakefs()        
        ## Restoring the CA certs
        self.fs.create_file(self.path)
        with open(self.path, "w") as fp:
            fp.write(self.certs)
        """
    def test__stock(self):
        try:
            d = h._stock("SBIN", date(2001,1,1), date(2001,1,31))
        except json.JSONDecodeError:
            pytest.skip("NSE returned non-JSON response while fetching stock history")
        if not d:
            pytest.skip("NSE returned empty stock history for requested range")
        assert d[0]["CH_TIMESTAMP"] >= d[-1]["CH_TIMESTAMP"]
        # Check if there's no data
        d = h._stock("SBIN", date(2020,7,4), date(2020,7,5))
        assert len(d) == 0
        # Check future date
        from_date = datetime.now().date() + timedelta(days=1)
        to_date = from_date + timedelta(days=10)
        d = h._stock("SBIN", from_date, to_date)
        assert len(d) == 0

    def test_stock_raw(self):
        from_date = date(2001,1,15)
        to_date = date(2002,1,15)
        d = nse.stock_raw("SBIN", from_date, to_date)
        if not d:
            pytest.skip("NSE returned empty stock history for stock_raw contract check")
        assert len(d) > 0
        all_dates = [datetime.strptime(k["CH_TIMESTAMP"], "%Y-%m-%d").date() for k in d]
        assert max(all_dates) <= to_date
        assert min(all_dates) >= from_date
        assert d[-1]["CH_TIMESTAMP"] <= d[0]["CH_TIMESTAMP"]
        app_name = nse.APP_NAME + '-stock'
        # Use the J_CACHE_DIR set in setup_test
        cache_dir = os.path.join('/fakecache', app_name, app_name)
        files = os.listdir(cache_dir)
        assert len(files) >= 1
    
    def test_stock_csv(self):
        from_date = date(2001,1,15)
        to_date = date(2002,1,15)
        raw = nse.stock_raw("SBIN", from_date, to_date)
        if not raw:
            pytest.skip("NSE returned empty stock history for stock_csv contract check")
        output = nse.stock_csv("SBIN", from_date, to_date)
        with open(output) as fp:
            text = fp.read()
            rows = [x.split(',') for x in text.split('\n')]
        headers = [   "DATE", "SERIES",
                        "OPEN", "HIGH",
                        "LOW", "PREV. CLOSE",
                        "LTP", "CLOSE",
                        "VWAP", "52W H", "52W L",
                        "VOLUME", "VALUE", "NO OF TRADES", "SYMBOL"]
        assert headers == rows[0]
        assert raw[0]['CH_TIMESTAMP'] == rows[1][0]
        assert raw[0]['CH_OPENING_PRICE'] == int(rows[1][2])

    def test_stock_df(self):
        from_date = date(2001,1,15)
        to_date = date(2002,1,15)
        raw = nse.stock_raw("SBIN", from_date, to_date)
        if not raw:
            pytest.skip("NSE returned empty stock history for stock_df contract check")
        df = nse.stock_df("SBIN", from_date, to_date)
        
        assert len(raw) == len(df)
        assert "DATE" in df.columns
        assert "OPEN" in df.columns
        assert df['DATE'].iloc[0] >= df['DATE'].iloc[-1]

class TestDerivatives(TestCase):
    def setUp(self):
        setup_test(self)
    

class TestIndexHistory(TestCase):
    def setUp(self):
        setup_test(self)
    
    @skip_httpbin
    def test__post(self):
        """Test HTTP POST with httpbin - skipped in CI due to retry logic incompatibility."""
        h = nse.NSEIndexHistory()
        h.base_url = "https://httpbin.org"
        h.path_map['mypath'] = '/post'
        params = {'a': 'b'}
        r = h._post_json("mypath", params=params)
        assert json.loads(r.json()['data']) == params
    
"""
    def test_index_raw(self):
        symbol = "NIFTY 50"
        from_date = date(2020, 6, 1)
        to_date = date(2020, 7, 30) 
        d = nse.index_raw(symbol, from_date, to_date)
        assert d[0]['Index Name'] == 'Nifty 50'
        assert d[0]['HistoricalDate'] == '30 Jul 2020'
        assert d[-1]['HistoricalDate'] == '01 Jun 2020'
        app_name = nse.APP_NAME + '-index'
        files = os.listdir(user_cache_dir(app_name, app_name))
        assert len(files) == 2
    
    def test_index_csv(self):    
        from_date = date(2001,1,15)
        to_date = date(2001,6,15)
        raw = nse.index_raw("NIFTY 50", from_date, to_date)
        output = nse.index_csv("NIFTY 50", from_date, to_date)
        with open(output) as fp:
            text = fp.read()
            rows = [x.split(',') for x in text.split('\n')]
            assert rows[1][2] == raw[0]['OPEN']

    def test_index_df(self):    
        from_date = date(2001,1,15)
        to_date = date(2001,6,15)
        index_df = nse.index_df("NIFTY 50", from_date, to_date)
        assert len(index_df) > 100 
        assert list(index_df.columns) == ['Index Name', 'INDEX_NAME', 'HistoricalDate', 'OPEN', 'HIGH',
       'LOW', 'CLOSE'] 
"""

def test_expiry_dates():
    dt = date(2020,1,1)
    expiry_dts = nse.expiry_dates(dt, "FUTIDX", "NIFTY")
    
    # Test that we get a list of dates
    assert isinstance(expiry_dts, list)
    assert len(expiry_dts) > 0
    
    # Test that all items are date objects
    for expiry in expiry_dts:
        assert isinstance(expiry, date)
        
    # Test that dates are in the future relative to input date
    for expiry in expiry_dts:
        assert expiry >= dt
        
    # Test that for NIFTY we get weekly expiries (should be multiple per month)
    # NIFTY should have weekly expiries, so we should get several dates
    assert len(expiry_dts) >= 3


