import re
import logging
import datetime
import time
import json
from lxml import etree

from congress.tasks import utils

from congress.tasks.bill_info import sponsor_for as sponsor_for_bill, action_for


def process_amendment(amdt_data, bill_id, options):
    amdt = build_amendment_json_dict(amdt_data, options)
    path = output_for_amdt(amdt["amendment_id"], "json")

    logging.info("[%s] Saving %s to %s..." % (bill_id, amdt["amendment_id"], path))

    # output JSON - so easy!
    utils.write(
        json.dumps(amdt, sort_keys=True, indent=2, default=utils.format_datetime), path
    )

    with open(output_for_amdt(amdt["amendment_id"], "xml"), "wb") as xml_file:
        xml_file.write(create_govtrack_xml(amdt, options))


def build_amendment_json_dict(amdt_dict, options):
    # good set of tests for each situation:
    # samdt712-113 - amendment to bill
    # samdt112-113 - amendment to amendment on bill
    # samdt4904-111 - amendment to treaty
    # samdt4922-111 - amendment to amendment to treaty

    amendment_id = build_amendment_id(
        amdt_dict["type"].lower(), amdt_dict["number"], amdt_dict["congress"]
    )

    amends_bill = amends_bill_for(amdt_dict.get("amendedBill"))  # almost always present
    amends_treaty = None  # amends_treaty_for(amdt_dict) # the bulk data does not provide amendments to treaties (THOMAS did)
    amends_amendment = amends_amendment_for(
        amdt_dict.get("amendedAmendment")
    )  # sometimes present
    if not amends_bill and not amends_treaty:
        raise Exception("Choked finding out what bill or treaty the amendment amends.")

    actions = actions_for(amdt_dict["actions"]["actions"])

    amdt = {
        "amendment_id": amendment_id,
        "amendment_type": amdt_dict["type"].lower(),
        "chamber": amdt_dict["type"][0].lower(),
        "number": int(amdt_dict["number"]),
        "congress": amdt_dict["congress"],
        "amends_bill": amends_bill,
        "amends_treaty": amends_treaty,
        "amends_amendment": amends_amendment,
        "sponsor": sponsor_for(
            amdt_dict["sponsors"]["item"][0], amdt_dict["type"].lower()
        ),
        "purpose": amdt_dict["purpose"][0]
        if type(amdt_dict.get("purpose")) is list
        else amdt_dict.get("purpose"),
        "introduced_at": amdt_dict["submittedDate"][:10],
        "actions": actions,
        "updated_at": amdt_dict["updateDate"][0]
        if type(amdt_dict["updateDate"]) is list
        else amdt_dict["updateDate"],
    }

    # duplicate attributes creates lists when parsed, this block deduplicates
    if "description" in amdt_dict:
        amdt["description"] = amdt_dict.get("description")
        if type(amdt_dict.get("description")) is list:
            amdt["description"] = amdt["description"][0]

    if amdt_dict['type'][0].lower() == 's' and "proposedDate" in amdt_dict:
        amdt['proposed_at'] = amdt_dict['proposedDate']

    # needs to come *after* the setting of introduced_at
    amdt["status"], amdt["status_at"] = amendment_status_for(amdt)

    return amdt


def create_govtrack_xml(amdt, options):
    govtrack_type_codes = {
        "hr": "h",
        "s": "s",
        "hres": "hr",
        "sres": "sr",
        "hjres": "hj",
        "sjres": "sj",
        "hconres": "hc",
        "sconres": "sc",
    }
    root = etree.Element("amendment")
    root.set("session", amdt["congress"])
    root.set("chamber", amdt["amendment_type"][0])
    root.set("number", str(amdt["number"]))
    root.set("updated", utils.format_datetime(amdt["updated_at"]))

    make_node = utils.make_node

    if amdt.get("amends_bill", None):
        make_node(
            root,
            "amends",
            None,
            type=govtrack_type_codes[amdt["amends_bill"]["bill_type"]],
            number=str(amdt["amends_bill"]["number"]),
            sequence=str(amdt["house_number"])
            if amdt.get("house_number", None)
            else "",
        )
    elif amdt.get("amends_treaty", None):
        make_node(
            root,
            "amends",
            None,
            type="treaty",
            number=str(amdt["amends_treaty"]["number"]),
        )

    make_node(root, "status", amdt["status"], datetime=amdt["status_at"])

    if amdt["sponsor"] and amdt["sponsor"]["type"] == "person":
        v = amdt["sponsor"]["bioguide_id"]
        if not options.get("govtrack", False):
            make_node(root, "sponsor", None, bioguide_id=v)
        else:
            v = str(utils.translate_legislator_id("bioguide", v, "govtrack"))
            make_node(root, "sponsor", None, id=v)
    elif amdt["sponsor"] and amdt["sponsor"]["type"] == "committee":
        make_node(root, "sponsor", None, committee=amdt["sponsor"]["name"])
    else:
        make_node(root, "sponsor", None)

    make_node(root, "offered", None, datetime=amdt["introduced_at"])

    make_node(root, "description", amdt["description"] if amdt.get("description") else amdt["purpose"])
    if amdt.get("description"):
        make_node(root, "purpose", amdt["purpose"])

    actions = make_node(root, "actions", None)
    for action in amdt["actions"]:
        a = make_node(
            actions,
            action["type"] if action["type"] in ("vote",) else "action",
            None,
            datetime=action["acted_at"],
        )
        if action["type"] == "vote":
            a.set("how", action["how"])
            a.set("result", action["result"])
            if action.get("roll") != None:
                a.set("roll", str(action["roll"]))
        if action.get("text"):
            make_node(a, "text", action["text"])
        if action.get("in_committee"):
            make_node(a, "committee", None, name=action["in_committee"])
        for cr in action["references"]:
            make_node(a, "reference", None, ref=cr["reference"], label=cr["type"])

    return etree.tostring(root, pretty_print=True)


def build_amendment_id(amdt_type, amdt_number, congress):
    return "%s%s-%s" % (amdt_type, amdt_number, congress)


def amends_bill_for(amends_bill):
    from congress.tasks.bills import build_bill_id

    bill_id = build_bill_id(
        amends_bill["type"].lower(), amends_bill["number"], amends_bill["congress"]
    )
    return {
        "bill_id": bill_id,
        "bill_type": amends_bill["type"].lower(),
        "congress": int(amends_bill["congress"]),
        "number": int(amends_bill["number"]),
    }


def amends_amendment_for(amends_amdt):
    if amends_amdt is None:
        return None

    amdt_id = build_amendment_id(
        amends_amdt["type"].lower(), amends_amdt["number"], amends_amdt["congress"]
    )
    return {
        "amendment_id": amdt_id,
        "amendment_type": amends_amdt.get("type", "").lower(),
        "congress": int(amends_amdt.get("congress", "")),
        "number": int(amends_amdt.get("number", "")),
        "purpose": amends_amdt.get("purpose", ""),
        "description": amends_amdt.get("description", ""),
    }


def actions_for(action_list):
    if action_list is None:
        return []  # no actions
    actions = [action_for(action) for action in action_list["item"]]
    parse_amendment_actions(actions)
    return actions


def parse_amendment_actions(actions):
    for action in actions:
        # House Vote
        m = re.match(
            r"On agreeing to the .* amendments? (\(.*\) )?(?:as (?:modified|amended) )?(Agreed to|Failed) (without objection|by [^\.:]+|by (?:recorded vote|the Yeas and Nays): (\d+) - (\d+)(, \d+ Present)? \(Roll [nN]o. (\d+)\))\.",
            action["text"],
        )
        if m:
            action["where"] = "h"
            action["type"] = "vote"
            action["vote_type"] = "vote"

            if m.group(2) == "Agreed to":
                action["result"] = "pass"
            else:
                action["result"] = "fail"

            action["how"] = m.group(3)
            if "recorded vote" in m.group(3) or "the Yeas and Nays" in m.group(3):
                action["how"] = "roll"
                action["roll"] = int(m.group(7))

        # Senate Vote
        m = re.match(
            r"(Motion to table )?Amendment SA \d+(?:, .*?)? (as modified )?(agreed to|not agreed to) in Senate by ([^\.:\-]+|Yea-Nay( Vote)?. (\d+) - (\d+)(, \d+ Present)?. Record Vote Number: (\d+))\.",
            action["text"],
        )
        if m:
            action["type"] = "vote"
            action["vote_type"] = "vote"
            action["where"] = "s"

            if m.group(3) == "agreed to":
                action["result"] = "pass"
                if m.group(
                    1
                ):  # is a motion to table, so result is sort of reversed.... eeek
                    action["result"] = "fail"
            else:
                if m.group(
                    1
                ):  # is a failed motion to table, so this doesn't count as a vote on agreeing to the amendment
                    continue
                action["result"] = "fail"

            action["how"] = m.group(4)
            if "Yea-Nay" in m.group(4):
                action["how"] = "roll"
                action["roll"] = int(m.group(9))

        # Withdrawn
        m = re.match(r"Proposed amendment SA \d+ withdrawn in Senate", action["text"])
        if m:
            action["type"] = "withdrawn"


def amendment_status_for(amdt):
    status = "offered"
    status_date = amdt["introduced_at"]

    for action in amdt["actions"]:
        if action["type"] == "vote":
            status = action["result"]  # 'pass', 'fail'
            status_date = action["acted_at"]
        if action["type"] == "withdrawn":
            status = "withdrawn"
            status_date = action["acted_at"]

    return status, status_date


def sponsor_for(sponsor, amendment_type):
    if sponsor.get("bioguideId") is None:
        # A committee can sponsor an amendment!
        # Change e.g. "Rules Committee" to "House Rules" for the committee name,
        # for backwards compatibility.
        name = re.sub(
            r"(.*) Committee$",
            ("House" if (amendment_type[0] == "h") else "Senate") + r" \1",
            sponsor["name"],
        )
        return {
            "type": "committee",
            "name": name,
            # "committee_id": None, # TODO
        }

    return sponsor_for_bill(sponsor)


def output_for_amdt(amendment_id, format):
    amendment_type, number, congress = utils.split_bill_id(amendment_id)
    return "%s/%s/amendments/%s/%s%s/%s" % (
        utils.data_dir(),
        congress,
        amendment_type,
        amendment_type,
        number,
        "data.%s" % format,
    )
