import requests
from bs4 import BeautifulSoup
import json
import pandas as pd
import numpy as np
import time
import pickle
import math
from tqdm import tqdm
import logging

logger = logging.getLogger(__name__)
logger.info('loading beer scraper library')


def start_ba_session(data, headers=None, url='https://www.beeradvocate.com/community/login/login/'):
    # Start the session
    if headers is None:
        headers = {}
    logger.info('starting requests session')
    session = requests.Session()

    # Login to BA session
    response = session.post(url, headers=headers, data=data)
    if response.status_code == 200:
        return session
    else:
        logger.error(f'requests session failed:{response.status_code}')


def get_beer_style_dict_from_json(filename):
    logger.info('opening json file')
    # Opening JSON file
    with open(filename) as json_file:
        return json.load(json_file)


def get_beer_and_brewery_id(beer_style, beer_sub_style, beer_style_id, session=None):
    if session is None:
        session = start_ba_session(data={})

    beer_df = pd.DataFrame({}, columns=['beer_style', 'beer_sub_style', 'beer_id', 'brewery_id'])

    # Gets total number of pages
    total_beer_items = 10000

    beer_list = []
    beer_item = 0
    while beer_item < total_beer_items:

        beer_style_url = 'https://www.beeradvocate.com/beer/styles/' + \
                         beer_style_id + '?sort=revsD&start=' + str(beer_item)

        response = session.get(beer_style_url)
        soup = BeautifulSoup(response.text, 'html.parser')
        table = soup.findAll("table")

        if beer_item == 0:
            data_row = table[0].findAll('tr')[0].find('b')
            total_beer_items = int(str(data_row).split("(out of ")[1].split(") -")[0])
            logger.info(f'total_beer_items: {total_beer_items}')

        data_rows = table[0].findAll('tr')[3:-1]

        # Retrieve segment of url link for each specific beer in the list
        for i in range(len(data_rows)):
            brewery_id, beer_id = tuple(
                data_rows[i].find('a')['href'].replace('/beer/profile/', '')[:-1].split('/'))

            beer_df = beer_df.append({'beer_style': beer_style,
                                      'beer_sub_style': beer_sub_style,
                                      'beer_sub_style_id': beer_style_id,
                                      'beer_id': beer_id,
                                      'brewery_id': brewery_id},
                                     ignore_index=True)
        logger.info(f'Shape of beer df: {beer_df.shape}')
        beer_item += 50
    return beer_df


def pickle_final_beer_style_dict(beer_style_dict):
    # pickle dictionary
    # note: timestamp used for name of each pickled dict
    filename = 'final_beer_dict.csv'
    with open(filename, 'wb') as f:
        pickle.dump(beer_style_dict, f)
        f.close()


def pickle_beer_style_dict(beer_style_dict):
    # pickle dictionary
    # note: timestamp used for name of each pickled dict
    filename = 'final_beer_dict.pkl'
    with open(filename, 'wb') as f:
        pickle.dump(beer_style_dict, f)
        f.close()


def csv_beer_style_dict(beer_style_dict):
    beer_style_df = pd.DataFrame.from_dict(beer_style_dict)
    beer_style_df.to_csv()
    return beer_style_df


def beer_soup(beer, session=None):
    if session is None:
        logger.error(f' session failed: session value is type {type(session)}')

    # gets beautiful soup object from beer url generated by brewery id and beer id
    beer_url = 'https://www.beeradvocate.com/beer/profile/' + beer['brewery_id'] + '/' + beer['beer_id'] + '/'
    response = session.get(beer_url)
    return BeautifulSoup(response.text, "html.parser")


def get_beer_and_brewery_name(soup):
    name_ele = soup.findAll(class_='titleBar')
    name_ele_ = str(name_ele[0]).replace('<br/>', '+-+-+')

    name_soup = BeautifulSoup(name_ele_, "html.parser")

    final_name_ele = name_soup.findAll(class_='titleBar')
    final_name_ele = final_name_ele[0].text.split('+-+-+')

    return final_name_ele[0].replace('\n', ''), final_name_ele[1].replace('\n', '')


def get_rating(soup):
    ba_score = soup.findAll(class_='ba-ravg')
    return round(float(ba_score[0].text), 2)


def get_rating_count(soup):
    rating_count = soup.findAll(class_='ba-ratings')
    return int(rating_count[0].text.replace(',', ''))


def get_review_count(soup):
    review_count = soup.findAll(class_='ba-reviews')
    return int(review_count[0].text.replace(',', ''))


def get_ba_score(soup):
    ba_score_string = 'n/a'
    score_box_info = soup.find('div', {'id': 'score_box'}).find_all('span')
    for i in range(len(score_box_info)):
        if 'SCORE' in score_box_info[i].text:
            ba_score_string = score_box_info[i + 1].text
            break

    if ba_score_string == 'n/a':
        return np.nan
    return round(float(ba_score_string), 2)


def get_abv(soup):
    abv_info = soup.findAll('div', {'id': 'info_box'})
    for item in abv_info:
        item = item.text.split('\n')
        len_item = len(item)
        abv_string = ''
        for i in range(len_item):
            if 'ABV:' in item[i]:
                abv_string = item[i + 1]
                break
        abv_ = abv_string.split(' ')[-1]
        if abv_ == 'n/a' or abv_ == 'listed':
            return np.nan
        return round(float(abv_.strip('%')), 2)


def get_beer_data(soup):
    beer_name, brewery_name = get_beer_and_brewery_name(soup)
    beer_rating = get_rating(soup)
    beer_rating_count = get_rating_count(soup)
    beer_review_count = get_review_count(soup)
    beer_ba_score = get_ba_score(soup)
    beer_abv = get_abv(soup)

    return {
        'name': beer_name,
        'brewery_name': brewery_name,
        'rating': beer_rating,
        'rating_count': beer_rating_count,
        'review_count': beer_review_count,
        'ba_score': beer_ba_score,
        'abv': beer_abv,
    }


def get_user_info_from_soup(soup):
    info_ = soup.findAll(class_='BAscore_norm')
    info_user = soup.find("div", {"id": "rating_fullview"}).findAll(class_='username')
    return info_user, info_


def create_ratings_list(beer, info_user, info_, beer_rating_id):
    temp_beer_rating_list = []
    user_list = []
    rating_list = []

    # get list of users
    for i in range(len(info_user)):
        if info_user[i].text is not '':
            user_list.append(info_user[i].text)
    user_list = user_list[:]

    # get list of ratings
    for i in range(len(info_)):
        uni_rating = float(info_[i].text)
        uni_rating2 = format(uni_rating, '.2f')
        rating_list.append(uni_rating2)

    # append users and ratings as tuple pair
    for i in range(len(user_list)):
        beer_rating_data = {
            'brewery_id': beer['brewery_id'],
            'beer_id': beer['beer_id'],
            'beer_rating_id': beer_rating_id,
            'user_name': user_list[i],
            'user_rating': rating_list[i]
        }

        temp_beer_rating_list.append(beer_rating_data)
        beer_rating_id += 1

    return temp_beer_rating_list, beer_rating_id


def create_beer_meta_dataframe(beer_df, session, test_count=None):

    final_beer_list = []
    beer_meta_id = 1
    for index, beer_row in tqdm(beer_df.iterrows()):
        if test_count is not None and index >= test_count:
            break
        soup = beer_soup(beer_row, session)

        beer_meta_data = {}
        beer_meta_data.update(beer_row)
        beer_meta_data.update(get_beer_data(soup))
        beer_meta_data['beer_meta_id'] = beer_meta_id

        final_beer_list.append(beer_meta_data)
        beer_meta_id += 1

    return pd.DataFrame(final_beer_list).set_index('beer_meta_id')


def create_beer_rating_dataframe(beer_df, session, test_count=None):
    final_beer_rating_list = []
    beer_rating_id = 1

    for index, beer_row in tqdm(beer_df.iterrows()):
        if test_count is not None and index >= test_count:
            break
        # Gets total number of pages placeholder
        total_review_items = 10000

        review_item = 0
        review_url = 'https://www.beeradvocate.com/beer/profile/' + beer_row['brewery_id'] + '/' + beer_row[
            'beer_id'] + '/' + '?view=beer&sort=&start=' + str(review_item)

        response = session.post(review_url, data={'hideRatings': 'N'})

        while review_item < total_review_items:
            if review_item % 100 == 0:
                print(review_item)
            review_url = 'https://www.beeradvocate.com/beer/profile/' + beer_row['brewery_id'] + '/' + beer_row[
                'beer_id'] + '/' + '?view=beer&sort=&start=' + str(review_item)
            response = session.get(review_url)

            html_doc = response.text
            soup = BeautifulSoup(html_doc, 'html.parser')

            if review_item == 0:

                rating = soup.findAll(class_='ba-ratings')
                rc = rating[0].text
                rating_count = int(rc.replace(',', ''))
                if rating_count < 50:
                    break

                # divide rating by 25 and round down to get number of iterations
                ct = math.ceil(rating_count / 25)

                # return last page (integer)
                total_review_items = ct * 25

            info_user, info_ = get_user_info_from_soup(soup)
            temp_beer_rating_list, beer_rating_id = create_ratings_list(beer_row, info_user, info_, beer_rating_id)
            final_beer_rating_list = final_beer_rating_list + temp_beer_rating_list

            review_item += 25
    return pd.DataFrame(final_beer_rating_list).set_index('beer_rating_id')






