import utils
import logging
import re
import json
from lxml import etree
import time
import datetime
import os
import os.path


def fetch_vote(vote_id, options):
    logging.info("\n[%s] Fetching..." % vote_id)

    vote_chamber, vote_number, vote_congress, vote_session_year = utils.split_vote_id(vote_id)

    if vote_chamber == "h":
        url = "http://clerk.house.gov/evs/%s/roll%03d.xml" % (vote_session_year, int(vote_number))
    else:
        session_num = int(vote_session_year) - utils.get_congress_first_year(int(vote_congress)) + 1
        url = "http://www.senate.gov/legislative/LIS/roll_call_votes/vote%d%d/vote_%d_%d_%05d.xml" % (int(vote_congress), session_num, int(vote_congress), session_num, int(vote_number))

    # fetch vote XML page
    body = utils.download(
        url,
        "%s/votes/%s/%s%s/%s%s.xml" % (vote_congress, vote_session_year, vote_chamber, vote_number, vote_chamber, vote_number),
        utils.merge(options, {'binary': True}),
    )

    if not body:
        return {'saved': False, 'ok': False, 'reason': "failed to download"}

    if options.get("download_only", False):
        return {'saved': False, 'ok': True, 'reason': "requested download only"}

    if "This vote was vacated" in body:
        # Vacated votes: 2011-484, 2012-327, ...
        # Remove file, since it may previously have existed with data.
        for f in (output_for_vote(vote_id, "json"), output_for_vote(vote_id, "xml")):
            if os.path.exists(f):
                os.unlink(f)
        return {'saved': False, 'ok': True, 'reason': "vote was vacated"}

    dom = etree.fromstring(body)

    vote = {
        'vote_id': vote_id,
        'chamber': vote_chamber,
        'congress': int(vote_congress),
        'session': vote_session_year,
        'number': int(vote_number),
        'updated_at': datetime.datetime.fromtimestamp(time.time()),
        'source_url': url,
    }

    # do the heavy lifting

    if vote_chamber == "h":
        parse_house_vote(dom, vote)
    elif vote_chamber == "s":
        parse_senate_vote(dom, vote)

    # output and return

    output_vote(vote, options)

    return {'ok': True, 'saved': True}


def output_vote(vote, options, id_type=None):
    logging.info("[%s] Writing to disk..." % vote['vote_id'])

    # output JSON - so easy!
    utils.write(
        json.dumps(vote, sort_keys=True, indent=2, default=utils.format_datetime),
        output_for_vote(vote["vote_id"], "json"),
        options=options
    )

    # What kind of IDs are we passed for Members of Congress?
    # For current data, we infer from the chamber. For historical data from voteview,
    # we're passed the type in id_type, which is set to "bioguide".
    if not id_type:
        id_type = ("bioguide" if vote["chamber"] == "h" else "lis")

    # output XML
    root = etree.Element("roll")

    root.set("where", "house" if vote['chamber'] == "h" else "senate")
    root.set("session", str(vote["congress"]))
    root.set("year", str(vote["date"].year))
    root.set("roll", str(vote["number"]))
    if "voteview" in vote["source_url"]:
        root.set("source", "keithpoole")
    else:
        root.set("source", "house.gov" if vote["chamber"] == "h" else "senate.gov")

    root.set("datetime", utils.format_datetime(vote['date']))
    root.set("updated", utils.format_datetime(vote['updated_at']))

    def get_votes(option):
        return len(vote["votes"].get(option, []))
    root.set("aye", str(get_votes("Yea") + get_votes("Aye")))
    root.set("nay", str(get_votes("Nay") + get_votes("No")))
    root.set("nv", str(get_votes("Not Voting")))
    root.set("present", str(get_votes("Present")))

    utils.make_node(root, "category", vote["category"])
    utils.make_node(root, "type", vote["type"])
    utils.make_node(root, "question", vote["question"])
    utils.make_node(root, "required", vote["requires"])
    utils.make_node(root, "result", vote["result"])

    if vote.get("bill"):
        govtrack_type_codes = {'hr': 'h', 's': 's', 'hres': 'hr', 'sres': 'sr', 'hjres': 'hj', 'sjres': 'sj', 'hconres': 'hc', 'sconres': 'sc'}
        utils.make_node(root, "bill", None, session=str(vote["bill"]["congress"]), type=govtrack_type_codes[vote["bill"]["type"]], number=str(vote["bill"]["number"]))

    if "amendment" in vote:
        n = utils.make_node(root, "amendment", None)
        if vote["amendment"]["type"] == "s":
            n.set("ref", "regular")
            n.set("session", str(vote["congress"]))
            n.set("number", "s" + str(vote["amendment"]["number"]))
        elif vote["amendment"]["type"] == "h-bill":
            n.set("ref", "bill-serial")
            n.set("session", str(vote["congress"]))
            n.set("number", str(vote["amendment"]["number"]))

    # well-known keys for certain vote types: +/-/P/0
    option_keys = {"Aye": "+", "Yea": "+", "Nay": "-", "No": "-", "Present": "P", "Not Voting": "0", "Guilty": "+", "Not Guilty": "-" }

    # preferred order of output: ayes, nays, present, then not voting, and similarly for guilty/not-guilty
    # and handling other options like people's names for votes for the Speaker.
    option_sort_order = ('Aye', 'Yea', 'Guilty', 'No', 'Nay', 'Not Guilty', 'OTHER', 'Present', 'Not Voting')
    options_list = sorted(vote["votes"].keys(), key=lambda o: option_sort_order.index(o) if o in option_sort_order else option_sort_order.index("OTHER"))
    for option in options_list:
        if option not in option_keys:
            option_keys[option] = option
        utils.make_node(root, "option", option, key=option_keys[option])

    for option in options_list:
        for v in vote["votes"][option]:
            n = utils.make_node(root, "voter", None)
            if v == "VP":
                n.set("id", "0")
                n.set("VP", "1")
            elif not options.get("govtrack", False):
                n.set("id", str(v["id"]))
            else:
                n.set("id", str(utils.get_govtrack_person_id(id_type, v["id"])))
            n.set("vote", option_keys[option])
            n.set("value", option)
            if v != "VP":
                n.set("state", v["state"])
                if v.get("voteview_votecode_extra") is not None:
                    n.set("voteview_votecode_extra", v["voteview_votecode_extra"])

    xmloutput = etree.tostring(root, pretty_print=True, encoding="utf8")

    # mimick two hard line breaks in GovTrack's legacy output to ease running diffs
    xmloutput = re.sub('(source=".*?") ', r"\1\n  ", xmloutput)
    xmloutput = re.sub('(updated=".*?") ', r"\1\n  ", xmloutput)

    utils.write(
        xmloutput,
        output_for_vote(vote['vote_id'], "xml"),
        options=options
    )


def output_for_vote(vote_id, format):
    vote_chamber, vote_number, vote_congress, vote_session_year = utils.split_vote_id(vote_id)
    return "%s/%s/votes/%s/%s%s/%s" % (utils.data_dir(), vote_congress, vote_session_year, vote_chamber, vote_number, "data.%s" % format)


def parse_senate_vote(dom, vote):
    def parse_date(d):
        return datetime.datetime.strptime(d, "%B %d, %Y, %I:%M %p")

    vote["date"] = parse_date(dom.xpath("string(vote_date)"))
    if len(dom.xpath("modify_date")) > 0:
        vote["record_modified"] = parse_date(dom.xpath("string(modify_date)"))  # some votes like s1-110.2008 don't have a modify_date
    vote["question"] = unicode(dom.xpath("string(vote_question_text)"))
    if vote["question"] == "":
        vote["question"] = unicode(dom.xpath("string(question)"))  # historical votes?
    vote["type"] = unicode(dom.xpath("string(vote_question)"))
    if vote["type"] == "":
        vote["type"] = vote["question"]
    vote["type"] = normalize_vote_type(vote["type"])
    vote["category"] = get_vote_category(vote["type"])
    vote["subject"] = unicode(dom.xpath("string(vote_title)"))
    vote["requires"] = unicode(dom.xpath("string(majority_requirement)"))
    vote["result_text"] = unicode(dom.xpath("string(vote_result_text)"))
    vote["result"] = unicode(dom.xpath("string(vote_result)"))

    bill_types = {"S.": "s", "S.Con.Res.": "sconres", "S.J.Res.": "sjres", "S.Res.": "sres", "H.R.": "hr", "H.Con.Res.": "hconres", "H.J.Res.": "hjres", "H.Res.": "hres"}

    if unicode(dom.xpath("string(document/document_type)")):
        if dom.xpath("string(document/document_type)") == "PN":
            vote["nomination"] = {
                "number": unicode(dom.xpath("string(document/document_number)")),
                "title": unicode(dom.xpath("string(document/document_title)")),
            }
            vote["question"] += ": " + vote["nomination"]["title"]
        elif dom.xpath("string(document/document_type)") == "Treaty Doc.":
            vote["treaty"] = {
                "title": unicode(dom.xpath("string(document/document_title)")),
            }
        else:
            vote["bill"] = {
                "congress": int(dom.xpath("number(document/document_congress|congress)")),  # some historical files don't have document/document_congress so take the first of document/document_congress or the top-level congress element as a fall-back