# -*- coding: utf-8 -*-
import datetime
import time
import praw
import sqlite3
import os
import vals
import sys
import unidecode
import numpy as np
import logger
import weekly_playlist
from tqdm import tqdm

global log
log = logger.get_logger(__name__) 

class HHHBot:
    def __init__(self):
        global log

        self.footer = '\n\n---\n\n^(This post was generated by a bot)\n\n^Subscribe ^to ^roundups: ^[[Daily](http://www.' \
                      'reddit.com/message/compose/?to={username}&subject=subscribe&message=daily)] ^[[Weekly](http://www.' \
                      'reddit.com/message/compose/?to={username}&subject=subscribe&message=weekly)] '\
                        '^[[Both](http://www.reddit.com/message/compose/?to={username}&subject=subscribe&message=both)] '\
                      '^[[Unsubscribe](http://www.reddit.com/message/compose/?to={username}' \
                      '&subject=unsubscribe&message=remove)]\n\n ^[[Feedback](http://www.reddit.com/message/compose/?to={admin}' \
                      '&amp;subject=%2Fu%2FHHHFreshBot2.0%20feedback;message=If%20you%20are%20providing%20feedback%20about%20a%20specific' \
                      '%20post%2C%20please%20include%20the%20link%20to%20that%20post.%20Thanks!)]'.format(
            username=vals.username, admin=vals.admin)

        self.db = sqlite3.connect(os.path.join(vals.cwd, "fresh.db"))

        self.c = self.db.cursor()
        self.c.execute("CREATE TABLE IF NOT EXISTS subscriptions(USER TEXT, SUBSCRIPTION TEXT)")
        self.c.execute(
            "CREATE TABLE IF NOT EXISTS posts (ID TEXT, TITLE TEXT, PERMA TEXT, URL TEXT, TIME INT, SCORE INT, SUBMITTER TEXT)")
        self.db.commit()

        self.r = praw.Reddit(client_id=vals.client_id, client_secret=vals.client_secret,
                             password=vals.password, username=vals.username, user_agent=vals.userAgent)
#        log.error("Test: No reddit instance set")
        log.setRedditInst(self.r)      
#        log.error("TEST: Reddit instance set")

        self.prawCharLimit = 10000*0.9

        self.sub_posting = self.r.subreddit(vals.subreddit)       
        self.hhh = self.r.subreddit(vals.hhh)

        self.today = datetime.datetime.utcnow().strftime('%A')
        self.ydat = (datetime.datetime.utcnow() - datetime.timedelta(1)).strftime('%A')

        # Config
        self.postScoreThreshold = 50
        self.fetchPostMaxAge = 7 * 24 * 60 * 60  # 1 day
        self.deletePostAge = 31 * 24 * 60 * 60  # 1 month

    def __del__(self):
        self.c.close()
        self.db.commit()
        self.db.close()

    def fetchNewPosts(self):
        for post in self.hhh.new(limit=5000):
            if '[fresh' in post.title.lower() and post.score > self.postScoreThreshold and time.time() - post.created_utc < self.fetchPostMaxAge:  # [fresh...] tag, high enough score, and not too old

                self.c.execute("SELECT * FROM posts WHERE ID=?", (post.id,))

                if self.c.fetchone() is None:
                    id_ = unidecode.unidecode(post.id)
                    title = unidecode.unidecode(post.title.replace("[", "\[").replace("]", "\]").replace("|", "\|"))
                    permalink = unidecode.unidecode('https://redd.it/' + id_)
                    url = unidecode.unidecode(post.url)
                    created = post.created_utc
                    score = post.score
                    submitter = unidecode.unidecode(post.author.name)


                    log.debug("Fresh post found - name {title}, id {id}, score {score}, age {age} hrs".format(
                        title=title,
                        id=id_,
                        score=score,
                        age=round((time.time() - created) / (60 * 60), 2)))
                    self.db.execute("INSERT INTO posts VALUES (?,?,?,?,?,?,?)",
                                    (id_, title, permalink, url, created, score, submitter))
        self.db.commit()

    def garbageDisposal(self):
        
        if vals.DEV:                
            self.c.execute("SELECT * FROM posts WHERE (TIME<{t}) OR (SCORE<{s} AND TIME<{t})".format(
                t=time.time() - self.deletePostAge,  # Select all posts where the unix time of the posts creation is lower than current time - posts creation time
                s=self.postScoreThreshold
            ))

            print(len(self.c.fetchall()))

        else:
            self.c.execute("DELETE FROM posts WHERE (TIME<{t}) OR (SCORE<{s} AND TIME<{t})".format(
                t=time.time() - self.deletePostAge,  # Select all posts where the unix time of the posts creation is lower than current time - posts creation time
                s=self.postScoreThreshold
            ))
            self.db.commit()

    def updateScore(self):
        log.debug("Starting update scores process")
        t = time.time()
        self.c.execute("SELECT * FROM posts")
        posts = self.c.fetchall()
        for row in tqdm(posts):
            id_ = row[0]
            oldScore = row[5]
            post = self.r.submission(id=id_)
            score = post.score
            if abs(score-oldScore)>20: #Only significant changes
                log.debug("Post id {id} updated to new score {score} (change of {change})".format(
                    id=id_,
                    score=score,
                    change=oldScore - score))
                self.db.execute("UPDATE posts SET SCORE=? WHERE ID=?", (score, id_))
        self.db.commit()
        log.debug("Finished update scores process in {}s".format(time.time()-t))

    def checkInbox(self):
        for pm in self.r.inbox.unread():
            response = ""

            subject = pm.subject.lower()
            body = pm.body.lower()
            try:
                author = pm.author.name
            except AttributeError:
                author = "None"

            if not pm.was_comment:
                no_resp = False
                if "unsubscribe" in subject:
                    if 'daily' in body:
                        response = self.unsubscribeUser(author, 'daily')
                    elif 'weekly' in body:
                        response = self.unsubscribeUser(author, 'weekly')
                    elif 'remove' in body:
                        response = self.unsubscribeUser(author, 'both')
                    else:
                        log.info("Unsubscribe message from {} could not be understood".format(author))
                        response = 'I couldn\'t understand your message. Please use one of the links below to subscribe!'

                elif "subscribe" in subject:
                    if 'daily' in body:
                        response = self.subscribeUser(author, 'daily')
                    elif 'weekly' in body:
                        response = self.subscribeUser(author, 'weekly')
                    elif 'both' in body:
                        response = self.subscribeUser(author, 'both')
                    else:
                        log.info("Subscribe message from {} could not be understood".format(author))
                        response = 'I couldn\'t understand your message. Please use one of the links below to subscribe!'

                else:
                    log.info("Message from {author} has been forwarded to admin".format(author=author))
                    try:
                        self.r.redditor(vals.admin).message('PM from /u/{}'.format(author),
                                                        "Message from /u/{author}\n\nSubject: {subject} \n\n---\n\n {body}".format(
                                                            author=author,
                                                            subject=subject,
                                                            body=body.decode('utf_8')
                                                        ) + self.footer)
                    except UnicodeEncodeError as err:
                        self.r.redditor(vals.admin).message('PM from /u/{} (error in code)'.format(author),
                                "Error in code {err}, check logs on RPi".format(err=err))
                    response = 'I received your message, but I\'m just a bot! I forwarded it to my admin {admin} who will take a look at it when he "\
                                "gets a chance.\n\nIf it\'s urgent, you should PM them directly.\n\nIf you\'re trying to subscribe to one of the roundups,"\
                                 "use the links below.\n\nThanks!'.format(admin=vals.admin)
                    no_resp = True
                #log.debug("no_resp = {}".format(no_resp))

                try:
                    if not no_resp:
                        pm.reply(response+self.footer)
                except Exception:
                    log.exception("Failed to send message to {} with body {}".format(author, response))
            else:
                log.info("Forwarding comment message to admin")
                self.r.redditor(vals.admin).message('Comment from /u/{}'.format(author),
                                                    'Message from /u/{author}\n\nSubject: {subject}\n\nContext: {context}\n\n---\n\n{body}'.format(
                                                        author=author,
                                                        subject=subject,
                                                        context=pm.context,
                                                        body=body
                                                    ))
            pm.mark_read()

    def subscribeUser(self, user, subscription):
        self.c.execute("SELECT * FROM subscriptions WHERE USER = ?", (user,))
        msg = ''
        val = self.c.fetchone()
        if val is None:
            self.c.execute("INSERT INTO subscriptions VALUES (?,?)", (user, subscription))
            log.info("Subscribed {} to {}".format(user, subscription))
            msg = "You have been subscribed to the {} mailing list".format(subscription)
        else:
                if val[1] == subscription:
                    log.info("User {} already subscribed to {} mailing list".format(user, subscription))
                    msg = "You are already subscribed to the {} mailing list!".format(subscription)
                else:
                    self.c.execute("UPDATE subscriptions SET SUBSCRIPTION=? WHERE USER=?", ("both", user))
                    log.info("Subscribed {} to both".format(user))
                    msg = "You have been subscribed to both mailing lists!"
        self.db.commit()
        return msg

    def unsubscribeUser(self, user, unsubscribeFrom):
        self.c.execute('SELECT * FROM subscriptions WHERE USER = ?', (user,))
        val = self.c.fetchone()
        msg = ''
        if val is None:
            msg = 'Unable to unsubscribe because you are not currently subscribed to any mailing lists.'
        else:
            if val[1] == "both" and unsubscribeFrom != "both":
                if unsubscribeFrom == "daily":
                    self.c.execute("UPDATE subscriptions SET SUBSCRIPTION=? WHERE USER=?", ("weekly", user))
                    msg = 'You have been unsubscribed from the daily mailing list.'
                    log.info("Unsubscribing {} from daily mailing lists".format(user))
                elif unsubscribeFrom == "weekly":
                    self.c.execute("UPDATE subscriptions SET SUBSCRIPTION=? WHERE USER=?", ("daily", user))
                    log.info("Unsubscribing {} from daily weekly lists".format(user))
                    msg = 'You have been unsubscribed from the weekly mailing list.'
            else:
                self.c.execute('DELETE FROM subscriptions WHERE USER = ?', (user,))
                log.info("Unsubscribing {} from all mailing lists".format(user))
                msg = 'You have been unsubscribed from both mailing lists. Sorry to see you go!'
        self.db.commit()
        return msg

    def generate(self, timeStart, timeEnd):

        self.c.execute("SELECT * FROM posts WHERE TIME<? AND TIME>? ORDER BY SCORE DESC", (timeStart,timeEnd))

        dict_ = {}


        for post in self.c:

            id_ = post[0]
            title = post[1].replace("|", ":") # backslash doesn't escape the | on reddit
            perma = post[2]
            url = post[3]
            t = post[4]
            key = datetime.datetime.utcfromtimestamp(t).strftime("%A, %B %-d, %Y")
            score = post[5]
            submitter = post[6]

            entry = '[{title}]({url}) | [link]({perma}) | {score} | /u/{submitter}\n'.format(title=title, url=url, perma=perma, score=score, submitter=submitter)

            if key not in dict_.keys():
                dict_[key] = []

            dict_[key].append(entry)


        msg = []
        for key in dict_.keys():
            text = ""
            text = text + "**"+key+"**" + "\n\nPost | link | Score | User \n :--|:--|:--|:--|\n"
            for item in dict_[key]:
                text += item
            text+="\n\n"
            msg.append((text,key,time.mktime(datetime.datetime.strptime(key, "%A, %B %d, %Y").timetuple())))

        msg.sort(key=lambda x: x[2])

        return msg

    def mailDaily(self):
        log.debug("mailDaily has been run")
        #self.updateScore()
        #log.debug("score has been updated")
        message = self.generate(time.time(), time.mktime(((datetime.datetime.utcnow()-datetime.timedelta(1)).timetuple())))
        intro = 'Welcome to The Daily [Fresh]ness! Fresh /r/hiphopheads posts delivered right to your inbox ever day.\n\n'

        text = intro
        for day in message:
            text += day[0]
        text += self.footer
        log.debug("Message has been selected")
        self.c.execute('SELECT * FROM subscriptions WHERE SUBSCRIPTION = ? OR SUBSCRIPTION = ?', ('daily', 'both',))
        log.debug("sqlite3 select command has been run")
        i = 0
        for row in self.c:
            log.debug("Mailing {} their daily message".format(row[0]))
            formattedDatetime = datetime.datetime.utcfromtimestamp(time.time()).strftime("%A, %B, %-d, %Y")
            try:
                self.r.redditor((row[0])).message("The Daily Freshness for {}".format(formattedDatetime), text) #message[0][1]), text)
            except Exception as e:
                log.error("Error caught: "+e)

            i+=1
        log.info("Sent {i} people their daily message".format(i=i))

    def mailWeekly(self):
        #self.updateScore()
	
        message = self.generate(time.time(), time.mktime(((datetime.datetime.utcnow()-datetime.timedelta(7)).timetuple())))
        intro = 'Welcome to The Weekly [Fresh]ness! Fresh /r/hiphopheads posts delivered right to your inbox every week.\n\n'

        text = intro
        parts = []
        for day in message:
            print(day[1], len(day[0]))
            tempText = text + day[0]
            if len(tempText) > self.prawCharLimit:
                parts.append(text)
                tempText = day[0]
            text = tempText
        parts.append(text) #To account for if the last day exceeds the prawCharLimit, or there is only one part

        parts[len(parts)-1]+=self.footer

        self.c.execute('SELECT * FROM subscriptions WHERE SUBSCRIPTION = ? OR SUBSCRIPTION = ?', ('weekly', 'both',))
        users= self.c.fetchall()
        i = 0
        for part in range(len(parts)):
            for row in users:
                if len(parts) == 1:
                    log.debug("Mailing {} their weekly message".format(row[0]))
                    self.r.redditor((row[0])).message("The Weekly Freshness for the week beginning {}".format(message[0][1]), parts[part])
                else:
                    log.debug("Mailing {} their weekly message part {}".format(row[0], part+1))
                    try:
                        self.r.redditor((row[0])).message("The Weekly Freshness for the week beginning {} : Part {}".format(message[0][1], part+1), parts[part])
                    except Exception as e:
                        log.error("Error caught:" + e)

                i+=1
        log.info("Sent {i} weekly messages to {u} people".format(i=i, u=len(users)))

    def postWeekly(self):
        #self.updateScore()
        if vals.DEV:
            sub = self.r.subreddit("testingsubforbot123")
        else:
            sub = self.sub_posting
        message = self.generate(time.time(), time.mktime(((datetime.datetime.utcnow()-datetime.timedelta(7)).timetuple())))

        try:
            playlist_url, len_found, len_total, perc = self.spotify_playlist()          
            intro = 'Welcome to The Weekly [Fresh]ness! Fresh /r/hiphopheads posts every week.\n\n[Spotify playlist]({}) with {}% ({}/{}) songs from this week! \n\n^This ^playlist ^is ^updated ^weekly!\n\n'.format(
            playlist_url, perc, len_found, len_total)
        except Exception as e:
            intro = 'Welcome to The Weekly [Fresh]ness! Fresh /r/hiphopheads posts every week.\n\n'
            log.error("An error has occured whilst updating the spotify playlist")
            log.error(e)
        text = intro
        parts = []
        for day in message:
            tempText = text + day[0]
            if len(tempText) > self.prawCharLimit:
                parts.append(text)
                tempText = day[0]
            text = tempText
        parts.append(text) #To account for if the last day exceeds the prawCharLimit, or there is only one part

        i = 0
        for part in range(len(parts)):
            if len(parts) == 1:
                sub.submit("The Weekly Freshness for the week beginning {}".format(message[0][1]), selftext=parts[part]+self.footer)
            else:
                if part == 0:
                    submission = sub.submit("The Weekly Freshness for the week beginning {}".format(message[0][1]), selftext="**Part {}**\n\n".format(part+1)+parts[part]+self.footer)
                else:
                    log.debug("Submitting part {}/{} to {}".format(part+1, len(parts), sub.display_name))
                    submission = submission.reply("**Part {}**\n\n".format(part+1)+parts[part]+self.footer)

            i+=1
        log.info("Submitted weekly freshness to {}".format(sub.display_name))

    def spotify_playlist(self):
        return weekly_playlist.weekly_playlist(self.c)


if __name__ == "__main__":
    triggers = ["getFresh", "mailDaily", "mailWeekly", "postWeekly", "checkMail", "help", "updatePlaylist"]
    log.debug("Starting {} with args {}".format(__name__,sys.argv))
    t = time.time()
    if len(sys.argv)==2:
        if sys.argv[1] in triggers:
            h = HHHBot()
            try:
                if sys.argv[1] == triggers[0]:
	                # Run this twice a day, takes forever to run....
                    h.fetchNewPosts()
                    h.updateScore()
                    h.checkInbox()
                elif sys.argv[1] == triggers[1]:
                    h.checkInbox()
                    h.mailDaily()
                elif sys.argv[1] == triggers[2]:
                    h.checkInbox()
                    h.mailWeekly()
                elif sys.argv[1] == triggers[3]:
   		    h.checkInbox()
        	    h.postWeekly()
                elif sys.argv[1] == triggers[4]:
               	    h.checkInbox()
                elif sys.argv[1] == triggers[6]:
                    # Not needed really, a call to spotify_playlist() is made in weekly post
                    #h.updateScore()
                    h.spotify_playlist()
            	    
                else:
                    print("\n".join([f for f in triggers]))
            except Exception as e:
                log.exception("Exception in main core of code... yikes")

        else:
            log.error("No correct argument was given")
    else:
        log.error("No argument was given")
    log.debug("Finishing {}. Time to complete was {}s".format(__name__, time.time()-t))
