#! /usr/bin/env python3
import argparse
import sys
import logging
import requests
import subprocess
from textwrap import dedent
from pathlib import Path
from datetime import datetime
from typing import Optional

try:
    import gitlab
except ImportError:
    print("Please install the 'python-gitlab' package from pip", file=sys.stderr)
    sys.exit(1)

TRELLO_KEY = "fba640a85f15c91e93e6b3f88e59489c"
TRELLO_BOARD_ID = "tw3Cn3L6"
TRELLO_LIST_ID = "6593166e9dd338621ed6848d"
TRELLO_TAGS = {
    "company": "64e03ac77d27032282436d28",  # Tag used to sort by company
    "waiting_to_merge": "65524315edf2d2edb0cc5d09",  # Tag used to indicate a MR is waiting to merge
    "draft": "65fdd81c83e5d6e00f1b9721", # Tag used to indicate a draft MR
}
GITLAB_ENDPOINT = "http://gitlab.guru-domain.gurustudio.com"
MY_ID = 64

logger = logging.getLogger(__name__)


def main() -> int:
    # Handle program arguments
    ap = argparse.ArgumentParser(
        prog="guru-sync-issues", description="Sync issues from GitLab to Trello"
    )
    ap.add_argument(
        "-v", "--verbose", help="Enable verbose logging", action="store_true"
    )
    args = ap.parse_args()

    # Configure logging
    logging.basicConfig(
        level=logging.DEBUG if args.verbose else logging.INFO,
        format="%(levelname)s:	%(message)s",
    )

    # Call `ewp-secrets` to obtain the GitLab token
    secrets_proc = subprocess.run(
        [
            (Path(__file__).parent / "ewp-secrets").as_posix(),
            "load",
            "-n",
            "gurustudio",
            "-k",
            "gitlab-pat",
        ],
        capture_output=True,
    )

    # If the secret manager failed, exit
    if secrets_proc.returncode != 0:
        print("Failed to load GitLab PAT", file=sys.stderr)
        print(
            "Please run `ewp-secrets store -n gurustudio -k gitlab-pat` to set a token",
            file=sys.stderr,
        )
        return 1

    # Extract the GitLab token
    gitlab_token = secrets_proc.stdout.decode().strip()

    # Call `ewp-secrets` to obtain the Trello API token
    secrets_proc = subprocess.run(
        [
            (Path(__file__).parent / "ewp-secrets").as_posix(),
            "load",
            "-n",
            "ewpratten",
            "-k",
            "trello-api-token",
        ],
        capture_output=True,
    )

    # If the secret manager failed, exit
    if secrets_proc.returncode != 0:
        print("Failed to load Trello API token", file=sys.stderr)
        print(
            "Please run `ewp-secrets store -n ewpratten -k trello-api-token` to set a token",
            file=sys.stderr,
        )
        return 1

    # Extract the Trello API token
    trello_api_token = secrets_proc.stdout.decode().strip()

    # Try to ping the gitlab server, and exit if it fails
    try:
        logger.debug("Attempting to connect to GitLab server")
        requests.get(GITLAB_ENDPOINT, timeout=2)
    except requests.exceptions.ConnectionError:
        logger.error("Could not connect to GitLab server")
        return 1

    # Authenticate with GitLab
    gitlab_client = gitlab.Gitlab(
        GITLAB_ENDPOINT, private_token=gitlab_token, user_agent="guru-sync-issues"
    )

    # Access the main group
    pp_group = gitlab_client.groups.get("pipeline-planning")

    # Find all open issues I'm assigned to
    open_issues = []
    open_issues.extend(
        gitlab_client.issues.list(state="opened", get_all=True, assignee_id=MY_ID)
    )
    open_issues.extend(
        pp_group.issues.list(state="opened", get_all=True, assignee_id=MY_ID)
    )

    # De-dupe the issues
    open_issues = list(set(open_issues))

    # Find all open MRs I'm assigned to
    open_mrs = []
    open_mrs.extend(
        gitlab_client.mergerequests.list(
            state="opened", get_all=True, assignee_id=MY_ID
        )
    )
    open_mrs.extend(
        pp_group.mergerequests.list(state="opened", get_all=True, assignee_id=MY_ID)
    )

    # De-dupe the MRs
    open_mrs = list(set(open_mrs))

    # Log findings
    logger.info(f"Found {len(open_issues)} open issues")
    logger.info(f"Found {len(open_mrs)} open merge requests")

    # Get all cards in Trello
    response = requests.get(
        f"https://api.trello.com/1/boards/{TRELLO_BOARD_ID}/cards",
        params={
            "key": TRELLO_KEY,
            "token": trello_api_token,
        },
    )
    response.raise_for_status()
    trello_cards = response.json()

    # Iterate over each GitLab actionable
    actionables = [("issue", issue) for issue in open_issues] + [
        ("merge request", mr) for mr in open_mrs
    ]
    for ty, issue in actionables:
        # Get the URL of the issue
        issue_url = issue.web_url

        # Check if there is a card that references this URL
        card = next((c for c in trello_cards if issue_url in c["desc"]), None)

        # If none exists, make a new card
        if not card:
            logger.info(f"Creating card for issue: {issue.title}")

            # Build params
            card_params = {
                "idList": TRELLO_LIST_ID,
                "name": issue.title,
                "key": TRELLO_KEY,
                "token": trello_api_token,
                "pos": "top",
                "desc": dedent(
                    f"""
                ## Linked GitLab Issues
                - {issue_url}
                
                ---
                """
                ),
                "idLabels": TRELLO_TAGS["company"],
            }

            # Make the card
            response = requests.post(
                "https://api.trello.com/1/cards",
                params=card_params,
            )
            response.raise_for_status()

            # Capture the card for later
            card = response.json()
            logger.info(f"Created card: {card['id']}")

        # Apply lables to MRs
        if ty == "merge request":
            if issue.title.startswith("Draft:") or issue.title.startswith("WIP:"):
                # Check if the card already has the 'Draft' label
                if any(
                    label["id"] == TRELLO_TAGS["draft"] for label in card["labels"]
                ):
                    continue
                
                logger.info(f"Adding 'Draft' label to card {card['id']}")
                response = requests.post(
                    f"https://api.trello.com/1/cards/{card['id']}/idLabels",
                    params={
                        "key": TRELLO_KEY,
                        "token": trello_api_token,
                        "value": TRELLO_TAGS["draft"],
                    },
                )
                response.raise_for_status()
            
            else:
                # Check if the card already has the 'Waiting to Merge' label
                if any(
                    label["id"] == TRELLO_TAGS["waiting_to_merge"] for label in card["labels"]
                ):
                    continue
                
                logger.info(f"Adding 'Waiting to Merge' label to card {card['id']}")
                response = requests.post(
                    f"https://api.trello.com/1/cards/{card['id']}/idLabels",
                    params={
                        "key": TRELLO_KEY,
                        "token": trello_api_token,
                        "value": TRELLO_TAGS["waiting_to_merge"],
                    },
                )
                response.raise_for_status()

        # Determine the due date assigned in GitLab
        if ty != "merge request":
            due_date: Optional[str] = issue.due_date
            if due_date:
                # Check if the card already has a due date
                card_due_date = card.get("due", None)
                if card_due_date:
                    card_due_date = card_due_date.split("T")[0]
                    
                # Convert to a useful format
                due_date = datetime.strptime(due_date, "%Y-%m-%d")
                card_due_date = datetime.strptime(card_due_date, "%Y-%m-%d") if card_due_date else None
                    
                # If the card has an earlier due date, skip
                if card_due_date and due_date < card_due_date:
                    logger.debug(f"Skipping due date update for {card['id']} because it has an earlier due date already")
                    continue
                
                # Update the due date
                logger.info(f"Updating due date for card {card['id']} to {due_date}")
                response = requests.put(
                    f"https://api.trello.com/1/cards/{card['id']}",
                    params={
                        "key": TRELLO_KEY,
                        "token": trello_api_token,
                        "due": due_date.isoformat(),
                    },
                )
            
        
    return 0


if __name__ == "__main__":
    sys.exit(main())