#! /usr/bin/env python3
import datetime
import os
import requests
import click

DATE_FORMAT = "%Y-%M-%d"


class JIRAClient(object):
    BASE_URL = "https://indicodata.atlassian.net"
    token = os.getenv("JIRA_API_TOKEN")
    DEFAULT_HEADERS = {"Authorization": f"Basic {token}"}
    BOARD_ID = 2

    @classmethod
    def _make_request(cls, method, path, **request_kwargs):
        response = getattr(requests, method)(
            cls.BASE_URL + path, headers=cls.DEFAULT_HEADERS, **request_kwargs
        )
        click.secho(
            f"{method.upper()}:{path} with status {response.status_code}",
            fg="bright_black",
        )

        if response.status_code >= 400:
            click.secho(f"{response.status_code}, {response.content}", fg="red")
        else:
            try:
                return response.json()
            except ValueError:
                return response.content

    @classmethod
    def get(cls, path, **request_kwargs):
        return cls._make_request("get", path, **request_kwargs)

    @classmethod
    def post(cls, path, **request_kwargs):
        return cls._make_request("post", path, **request_kwargs)

    @classmethod
    def put(cls, path, **request_kwargs):
        return cls._make_request("put", path, **request_kwargs)

    @classmethod
    def delete(cls, path, **request_kwargs):
        return cls._make_request("delete", path, **request_kwargs)

    @classmethod
    def get_current_sprint(cls, board_id=BOARD_ID):
        return cls.get(
            f"/rest/agile/1.0/board/{board_id}/sprint", params={"state": "active"}
        )["values"][0]

    @classmethod
    def move_ticket_to_sprint(cls, tickets, sprint_id):
        return cls.post(
            f"/rest/agile/1.0/sprint/{sprint_id}/issue",
            json={"issues": [f"DEV-{ticket}" for ticket in tickets]},
        )

    @classmethod
    def get_tickets_by_type(cls, ticket_type, sprint_id=None, board_id=BOARD_ID):
        return cls.get(
            f"/rest/agile/1.0/board/{board_id}/issue",
            params={"jql": f'issueType = {ticket_type} AND status = "In Progress"'},
        )

    @classmethod
    def get_project_versions(cls):
        versions = cls.get("/rest/api/2/project/DEV/versions")
        return sorted(
            [x for x in versions if not x["released"] and not x["archived"]],
            key=lambda x: x["releaseDate"],
        )

    @classmethod
    def get_next_version(cls):
        versions = cls.get_project_versions()
        for version in versions:
            version["version_date"] = datetime.datetime.strptime(
                version["releaseDate"], DATE_FORMAT
            )

        sorted_versions = sorted(versions, key=lambda v: v["version_date"])

        now = datetime.datetime.now()
        return next(
            (v for v in sorted_versions if v["version_date"] > now), sorted_versions[-1]
        )

    @classmethod
    def assign_version_to_issue(cls, ticket, version):
        return cls.put(
            f"/rest/api/2/issue/{ticket}",
            json={"update": {"fixVersions": [{"set": [{"id": version}]}]}},
        )

    @classmethod
    def block_issue(cls, blocked, blocking):
        cls.put(
            f"/rest/api/2/issue/{blocking}",
            json={
                "update": {
                    "issuelinks": [
                        {
                            "add": {
                                "type": {
                                    "name": "Blocks",
                                    "inward": "is blocked by",
                                    "outward": "blocks",
                                },
                                "outwardIssue": {"key": blocked},
                            }
                        }
                    ]
                }
            },
        )


def print_versions(versions):
    click.secho("Open Release Versions")
    for idx, version in enumerate(versions):
        click.secho(
            f"{idx}: Date: {version.get('releaseDate')} - Name: {version['name']}  https://indicodata.atlassian.net/projects/DEV/versions/{version['id']}/tab/release-report-all-issues",
            fg="cyan",
        )


@click.group()
def main():
    pass


@main.command("mtcs")
@click.argument("tickets", nargs=-1)
def move_to_current_sprint(tickets):
    """
    Argument [tickets] as space separated DEV-{ticket} values
    Result:
        You should get a 204 status result and the tickets will be moved into the currently active sprint.
    """
    sprint = JIRAClient.get_current_sprint()
    click.secho(f"Moving {tickets} to sprint {sprint['name']}", fg="green")
    JIRAClient.move_ticket_to_sprint(tickets, sprint["id"])


@main.command("releases")
def get_releases_release():
    versions = JIRAClient.get_project_versions()
    print_versions(versions)


@main.command("release")
def get_current_release():
    versions = JIRAClient.get_next_version()
    print_versions([versions])


@main.command("atr")
@click.argument("tickets", nargs=-1)
def add_to_current_release(tickets):
    version = JIRAClient.get_next_version()
    if not version:
        click.secho("No project release version found", fg="red")
        return

    for ticket in tickets:
        if not ticket.startswith("DEV"):
            ticket = f"DEV-{ticket}"

        JIRAClient.assign_version_to_issue(ticket, version["id"])


if __name__ == "__main__":
    main()

