"""
Nominations API for the Congressional MCP server.

This module provides access to nomination data from the Congress.gov API.
"""

import logging
from typing import Dict, Any, List, Optional
from mcp.server.fastmcp import Context
from ..mcp_app import mcp
from ..core.api_wrapper import safe_congressional_request
from ..core.validators import ParameterValidator
from ..core.exceptions import CommonErrors, format_error_response
from ..core.response_utils import ResponseProcessor

# Set up logger
logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)

# Initialize validator
validator = ParameterValidator()

# Base endpoint for the nominations API
BASE_ENDPOINT = "nomination"

# Formatting helpers
def format_nomination_item(nomination: Dict[str, Any]) -> str:
    """Format a nomination for display in a list."""
    lines = []
    
    # Citation (PN number)
    lines.append(f"Citation: {nomination.get('citation', 'N/A')}")
    
    # Congress
    lines.append(f"Congress: {nomination.get('congress', 'N/A')}")
    
    # Nomination Number
    lines.append(f"Nomination Number: {nomination.get('number', 'N/A')}")
    
    # Organization (e.g., Army, Navy, etc.)
    if "organization" in nomination:
        lines.append(f"Organization: {nomination['organization']}")
    
    # Is Military
    is_military = nomination.get('nominationType', {}).get('isMilitary', False)
    lines.append(f"Military Nomination: {'Yes' if is_military else 'No'}")
    
    # Received Date
    if "receivedDate" in nomination:
        lines.append(f"Received Date: {nomination['receivedDate']}")
    
    # Latest Action
    if "latestAction" in nomination:
        action = nomination["latestAction"]
        action_date = action.get("actionDate", "N/A")
        action_text = action.get("text", "N/A")
        lines.append(f"Latest Action ({action_date}): {action_text}")
    
    # Update Date
    if "updateDate" in nomination:
        lines.append(f"Update Date: {nomination['updateDate']}")
    
    # URL
    if "url" in nomination:
        lines.append(f"URL: {nomination['url']}")
    
    return "\n".join(lines)

def format_nomination_detail(nomination: Dict[str, Any]) -> str:
    """Format detailed information for a nomination."""
    lines = []
    
    # Citation (PN number)
    lines.append(f"Nomination - {nomination.get('citation', 'N/A')}")
    
    # Congress
    lines.append(f"Congress: {nomination.get('congress', 'N/A')}")
    
    # Nomination Number
    lines.append(f"Nomination Number: {nomination.get('number', 'N/A')}")
    
    # Part Number
    if "partNumber" in nomination:
        lines.append(f"Part Number: {nomination['partNumber']}")
    
    # Is List
    is_list = nomination.get('isList', False)
    lines.append(f"Is List Nomination: {'Yes' if is_list else 'No'}")
    
    # Received Date
    if "receivedDate" in nomination:
        lines.append(f"Received Date: {nomination['receivedDate']}")
    
    # Latest Action
    if "latestAction" in nomination:
        action = nomination["latestAction"]
        action_date = action.get("actionDate", "N/A")
        action_text = action.get("text", "N/A")
        lines.append(f"\nLatest Action ({action_date}): {action_text}")
    
    # Nominees
    if "nominees" in nomination and nomination["nominees"]:
        lines.append("\nNominees:")
        for idx, nominee in enumerate(nomination["nominees"], 1):
            lines.append(f"  {idx}. Position: {nominee.get('positionTitle', 'N/A')}")
            lines.append(f"     Organization: {nominee.get('organization', 'N/A')}")
            lines.append(f"     Nominee Count: {nominee.get('nomineeCount', 'N/A')}")
            if "introText" in nominee:
                intro_text = nominee.get('introText', '')
                if len(intro_text) > 100:
                    intro_text = intro_text[:97] + "..."
                lines.append(f"     Intro: {intro_text}")
    
    # Actions count and URL
    if "actions" in nomination:
        actions = nomination["actions"]
        lines.append(f"\nActions: {actions.get('count', 0)}")
        if "url" in actions:
            lines.append(f"Actions URL: {actions['url']}")
    
    # Committees count and URL
    if "committees" in nomination:
        committees = nomination["committees"]
        lines.append(f"\nCommittees: {committees.get('count', 0)}")
        if "url" in committees:
            lines.append(f"Committees URL: {committees['url']}")
    
    # Update Date
    if "updateDate" in nomination:
        lines.append(f"\nUpdate Date: {nomination['updateDate']}")
    
    return "\n".join(lines)

def format_nominees_list(nominees: List[Dict[str, Any]]) -> str:
    """Format a list of nominees."""
    if not nominees:
        return "No nominees found."
    
    lines = ["Nominees:"]
    
    for nominee in nominees:
        lines.append("")
        lines.append(f"First Name: {nominee.get('firstName', 'N/A')}")
        lines.append(f"Middle Name: {nominee.get('middleName', 'N/A')}")
        lines.append(f"Last Name: {nominee.get('lastName', 'N/A')}")
        lines.append(f"Ordinal: {nominee.get('ordinal', 'N/A')}")
    
    return "\n".join(lines)

def format_nomination_actions(actions: List[Dict[str, Any]]) -> str:
    """Format a list of actions for a nomination."""
    if not actions:
        return "No actions found."
    
    lines = ["Nomination Actions:"]
    
    for action in actions:
        lines.append("")
        lines.append(f"Date: {action.get('actionDate', 'N/A')}")
        lines.append(f"Type: {action.get('type', 'N/A')}")
        lines.append(f"Text: {action.get('text', 'N/A')}")
        
        if "committees" in action and action["committees"]:
            lines.append("Committees:")
            for committee in action["committees"]:
                lines.append(f"  - {committee.get('name', 'N/A')} ({committee.get('systemCode', 'N/A')})")
    
    return "\n".join(lines)

def format_nomination_committees(ctx: Context, committees: List[Dict[str, Any]]) -> str:
    """Format a list of committees for a nomination."""
    if not committees:
        return "No committees found."
    
    lines = ["Nomination Committees:"]
    
    for committee in committees:
        lines.append("")
        lines.append(f"Name: {committee.get('name', 'N/A')}")
        lines.append(f"Chamber: {committee.get('chamber', 'N/A')}")
        lines.append(f"Type: {committee.get('type', 'N/A')}")
        lines.append(f"System Code: {committee.get('systemCode', 'N/A')}")
        
        if "activities" in committee and committee["activities"]:
            lines.append("Activities:")
            for activity in committee["activities"]:
                lines.append(f"  - {activity.get('name', 'N/A')} ({activity.get('date', 'N/A')})")
    
    return "\n".join(lines)

def format_nomination_hearings(hearings: List[Dict[str, Any]]) -> str:
    """Format a list of hearings for a nomination."""
    if not hearings:
        return "No hearings found."
    
    lines = ["Nomination Hearings:"]
    
    for hearing in hearings:
        lines.append("")
        lines.append(f"Chamber: {hearing.get('chamber', 'N/A')}")
        lines.append(f"Citation: {hearing.get('citation', 'N/A')}")
        lines.append(f"Date: {hearing.get('date', 'N/A')}")
        lines.append(f"Jacket Number: {hearing.get('jacketNumber', 'N/A')}")
        lines.append(f"Number: {hearing.get('number', 'N/A')}")
    
    return "\n".join(lines)

# MCP Tools
# @require_paid_access
async def get_latest_nominations(ctx: Context) -> str:
    """
    Get the most recent nominations.
    Returns the 10 most recently published nominations by default.
    """
    logger.info("Getting latest nominations")
    
    # Make safe API request
    endpoint = BASE_ENDPOINT
    data = await safe_congressional_request(endpoint, ctx, {}, endpoint_type='nominations')
    
    if "error" in data:
        logger.error(f"Error getting latest nominations: {data['error']}")
        return format_error_response(CommonErrors.api_server_error(f"latest nominations: {endpoint}", message=data['error']))
    
    if 'nominations' not in data:
        logger.warning("No nominations field in response")
        return "No nominations found."
    
    nominations = data['nominations']
    if not nominations:
        logger.info("No nominations found")
        return "No nominations found."
    
    # Deduplicate results
    nominations = ResponseProcessor.deduplicate_results(nominations, ['number', 'congress'])
    
    logger.info(f"Found {len(nominations)} latest nominations")
    
    # Format results
    lines = ["# Latest Nominations\n"]
    for idx, nomination in enumerate(nominations, 1):
        lines.append(f"## {idx}. {format_nomination_item(nomination)}\n")
    
    return "\n".join(lines)

# @require_paid_access
async def get_nominations_by_congress(ctx: Context, congress: int) -> str:
    """
    Get nominations for a specific Congress.
    
    Args:
        congress: The Congress number (e.g., 117 for the 117th Congress)
    """
    logger.info(f"Getting nominations for Congress {congress}")
    
    # Validate parameters
    congress_validation = ParameterValidator.validate_congress_number(congress)
    if not congress_validation.is_valid:
        logger.warning(f"Invalid congress number: {congress}")
        return format_error_response(CommonErrors.invalid_congress_number(congress))
    
    # Make safe API request
    endpoint = f"{BASE_ENDPOINT}/{congress}"
    data = await safe_congressional_request(endpoint, ctx, {}, endpoint_type='nominations')
    
    if "error" in data:
        logger.error(f"Error getting nominations for Congress {congress}: {data['error']}")
        return format_error_response(CommonErrors.api_server_error(f"nominations by congress: {endpoint}", message=data['error']))
    
    if 'nominations' not in data:
        logger.warning(f"No nominations field in response for Congress {congress}")
        return f"No nominations found for Congress {congress}."
    
    nominations = data['nominations']
    if not nominations:
        logger.info(f"No nominations found for Congress {congress}")
        return f"No nominations found for Congress {congress}."
    
    # Deduplicate results
    nominations = ResponseProcessor.deduplicate_results(nominations, ['number', 'congress'])
    
    logger.info(f"Found {len(nominations)} nominations for Congress {congress}")
    
    # Format results
    lines = [f"# Nominations for Congress {congress}\n"]
    for idx, nomination in enumerate(nominations, 1):
        lines.append(f"## {idx}. {format_nomination_item(nomination)}\n")
    
    return "\n".join(lines)

# @require_paid_access
async def get_nomination_details(ctx: Context, congress: int, nomination_number: int) -> str:
    """
    Get detailed information about a specific nomination.

    Args:
        congress: Congress number (e.g., 117 for 117th Congress)
        nomination_number: The nomination number
    """
    logger.info(f"Getting nomination details for {nomination_number}, Congress {congress}")
    
    # Validate parameters
    congress_validation = ParameterValidator.validate_congress_number(congress)
    if not congress_validation.is_valid:
        logger.warning(f"Invalid congress number: {congress}")
        return format_error_response(CommonErrors.invalid_congress_number(congress))
    
    if nomination_number <= 0:
        return format_error_response(CommonErrors.invalid_parameter(
            "nomination_number", nomination_number, "Nomination number must be a positive integer"
        ))
    
    # Make safe API request
    endpoint = f"{BASE_ENDPOINT}/{congress}/{nomination_number}"
    data = await safe_congressional_request(endpoint, ctx, {}, endpoint_type='nominations')
    
    if "error" in data:
        logger.error(f"Error getting nomination details for {nomination_number}, Congress {congress}: {data['error']}")
        return format_error_response(CommonErrors.api_server_error(f"nomination details: {endpoint}", message=data['error']))
    
    if 'nomination' not in data:
        logger.warning(f"No nomination field in response for {congress}/{nomination_number}")
        return f"No nomination found for nomination {nomination_number}, Congress {congress}."
    
    nomination = data['nomination']
    logger.info(f"Found nomination details for {nomination_number}, Congress {congress}")
    
    # Format and return the result
    return format_nomination_detail(nomination)

# @require_paid_access
async def get_nomination_nominees(ctx: Context, congress: int, nomination_number: int, ordinal: int) -> str:
    """
    Get nominees for a specific position within a nomination.
    
    Args:
        congress: The Congress number (e.g., 117 for the 117th Congress)
        nomination_number: The nomination number
        ordinal: The ordinal number for the position
    """
    logger.info(f"Getting nominees for nomination {nomination_number}, Congress {congress}, ordinal {ordinal}")
    
    # Validate parameters
    congress_validation = ParameterValidator.validate_congress_number(congress)
    if not congress_validation.is_valid:
        logger.warning(f"Invalid congress number: {congress}")
        return format_error_response(CommonErrors.invalid_congress_number(congress))
    
    if nomination_number <= 0:
        return format_error_response(CommonErrors.invalid_parameter(
            "nomination_number", nomination_number, "Nomination number must be a positive integer"
        ))
    
    if ordinal <= 0:
        return format_error_response(CommonErrors.invalid_parameter(
            "ordinal", ordinal, "Ordinal must be a positive integer"
        ))
    
    # Make safe API request
    endpoint = f"{BASE_ENDPOINT}/{congress}/{nomination_number}/{ordinal}"
    data = await safe_congressional_request(endpoint, ctx, {}, endpoint_type='nominations')
    
    if "error" in data:
        logger.error(f"Error getting nominees for nomination {nomination_number}, Congress {congress}, ordinal {ordinal}: {data['error']}")
        return format_error_response(CommonErrors.api_server_error(f"nomination nominees: {endpoint}", message=data['error']))
    
    if 'nominees' not in data:
        logger.warning(f"No nominees field in response for {congress}/{nomination_number}/{ordinal}")
        return f"No nominees found for nomination {nomination_number}, Congress {congress}, ordinal {ordinal}."
    
    nominees = data['nominees']
    if not nominees:
        logger.info(f"No nominees found for nomination {nomination_number}, Congress {congress}, ordinal {ordinal}")
        return f"No nominees found for nomination {nomination_number}, Congress {congress}, ordinal {ordinal}."
    
    # Deduplicate results
    nominees = ResponseProcessor.deduplicate_results(nominees, ['name', 'position'])
    
    logger.info(f"Found {len(nominees)} nominees for nomination {nomination_number}, Congress {congress}, ordinal {ordinal}")
    
    # Format the results
    return format_nominees_list(nominees)

# @require_paid_access
async def get_nomination_actions(ctx: Context, congress: int, nomination_number: int) -> str:
    """
    Get actions for a specific nomination.
    
    Args:
        congress: The Congress number (e.g., 117 for the 117th Congress)
        nomination_number: The nomination number
    """
    logger.info(f"Getting actions for nomination {nomination_number}, Congress {congress}")
    
    # Validate parameters
    congress_validation = ParameterValidator.validate_congress_number(congress)
    if not congress_validation.is_valid:
        return format_error_response(CommonErrors.invalid_parameter(
            "congress", congress, congress_validation.error_message
        ))
    
    if nomination_number <= 0:
        return format_error_response(CommonErrors.invalid_parameter(
            "nomination_number", nomination_number, "Nomination number must be a positive integer"
        ))
    
    # Make safe API request
    endpoint = f"{BASE_ENDPOINT}/{congress}/{nomination_number}/actions"
    data = await safe_congressional_request(endpoint, ctx, {}, endpoint_type='nominations')
    
    if "error" in data:
        logger.error(f"Error getting actions for nomination {nomination_number}, Congress {congress}: {data['error']}")
        return format_error_response(CommonErrors.api_server_error(f"nomination actions: {endpoint}", message=data['error']))
    
    if 'actions' not in data:
        logger.warning(f"No actions field in response for {congress}/{nomination_number}/actions")
        return f"No actions found for nomination {nomination_number}, Congress {congress}."
    
    actions = data['actions']
    if not actions:
        logger.info(f"No actions found for nomination {nomination_number}, Congress {congress}")
        return f"No actions found for nomination {nomination_number}, Congress {congress}."
    
    # Deduplicate actions
    deduplicated_actions = ResponseProcessor.deduplicate_results(
        actions,
        key_fields=['actionDate', 'text']
    )
    
    logger.info(f"Found {len(deduplicated_actions)} unique actions for nomination {nomination_number}, Congress {congress}")
    
    # Format the results
    return format_nomination_actions(deduplicated_actions)

# @require_paid_access
async def get_nomination_committees(ctx: Context, congress: int, nomination_number: int) -> str:
    """
    Get committees for a specific nomination.
    
    Args:
        congress: The Congress number (e.g., 117 for the 117th Congress)
        nomination_number: The nomination number
    """
    logger.info(f"Getting committees for nomination {nomination_number}, Congress {congress}")
    
    # Validate parameters
    congress_validation = ParameterValidator.validate_congress_number(congress)
    if not congress_validation.is_valid:
        return format_error_response(CommonErrors.invalid_parameter(
            "congress", congress, congress_validation.error_message
        ))
    
    if nomination_number <= 0:
        return format_error_response(CommonErrors.invalid_parameter(
            "nomination_number", nomination_number, "Nomination number must be a positive integer"
        ))
    
    # Make safe API request
    endpoint = f"{BASE_ENDPOINT}/{congress}/{nomination_number}/committees"
    data = await safe_congressional_request(endpoint, ctx, {}, endpoint_type='nominations')
    
    if "error" in data:
        logger.error(f"Error getting committees for nomination {nomination_number}, Congress {congress}: {data['error']}")
        return format_error_response(CommonErrors.api_server_error(f"nomination committees: {endpoint}", message=data['error']))
    
    if 'committees' not in data:
        logger.warning(f"No committees field in response for {congress}/{nomination_number}/committees")
        return f"No committees found for nomination {nomination_number}, Congress {congress}."
    
    committees = data['committees']
    if not committees:
        logger.info(f"No committees found for nomination {nomination_number}, Congress {congress}")
        return f"No committees found for nomination {nomination_number}, Congress {congress}."
    
    # Deduplicate committees
    deduplicated_committees = ResponseProcessor.deduplicate_results(
        committees,
        key_fields=['systemCode', 'name']
    )
    
    logger.info(f"Found {len(deduplicated_committees)} unique committees for nomination {nomination_number}, Congress {congress}")
    
    # Format the results
    return format_nomination_committees(ctx, deduplicated_committees)

# @require_paid_access
async def get_nomination_hearings(ctx: Context, congress: int, nomination_number: int) -> str:
    """
    Get hearings for a specific nomination.
    
    Args:
        congress: The Congress number (e.g., 117 for the 117th Congress)
        nomination_number: The nomination number
    """
    logger.info(f"Getting hearings for nomination {nomination_number}, Congress {congress}")
    
    # Validate parameters
    congress_validation = ParameterValidator.validate_congress_number(congress)
    if not congress_validation.is_valid:
        return format_error_response(CommonErrors.invalid_parameter(
            "congress", congress, congress_validation.error_message
        ))
    
    if nomination_number <= 0:
        return format_error_response(CommonErrors.invalid_parameter(
            "nomination_number", nomination_number, "Nomination number must be a positive integer"
        ))
    
    # Make safe API request
    endpoint = f"{BASE_ENDPOINT}/{congress}/{nomination_number}/hearings"
    data = await safe_congressional_request(endpoint, ctx, {}, endpoint_type='nominations')
    
    if "error" in data:
        logger.error(f"Error getting hearings for nomination {nomination_number}, Congress {congress}: {data['error']}")
        return format_error_response(CommonErrors.api_server_error(f"nomination hearings: {endpoint}", message=data['error']))
    
    if 'hearings' not in data:
        logger.warning(f"No hearings field in response for {congress}/{nomination_number}/hearings")
        return f"No hearings found for nomination {nomination_number}, Congress {congress}."
    
    hearings = data['hearings']
    if not hearings:
        logger.info(f"No hearings found for nomination {nomination_number}, Congress {congress}")
        return f"No hearings found for nomination {nomination_number}, Congress {congress}."
    
    # Deduplicate hearings
    deduplicated_hearings = ResponseProcessor.deduplicate_results(
        hearings,
        key_fields=['jacketNumber', 'date']
    )
    
    logger.info(f"Found {len(deduplicated_hearings)} unique hearings for nomination {nomination_number}, Congress {congress}")
    
    # Format the results
    return format_nomination_hearings(deduplicated_hearings)

# MCP Tool
# @require_paid_access
async def search_nominations(
    ctx: Context,
    keywords: Optional[str] = None,
    congress: Optional[int] = None,
    limit: int = 10,
    sort: str = "updateDate+desc",
    from_date: Optional[str] = None,
    to_date: Optional[str] = None
) -> str:
    """
    Search for nominations based on keywords.
    
    Args:
        keywords: Keywords to search for in nomination information
        congress: Optional Congress number (e.g., 117)
        limit: Maximum number of results to return (default: 10)
        sort: Sort order (default: "updateDate+desc")
        from_date: Optional start date for filtering (format: YYYY-MM-DDT00:00:00Z)
        to_date: Optional end date for filtering (format: YYYY-MM-DDT00:00:00Z)
    """
    logger.info(f"Searching for nominations with keywords: {keywords}, congress: {congress}")
    
    # Validate parameters
    if congress is not None:
        congress_validation = ParameterValidator.validate_congress_number(congress)
        if not congress_validation.is_valid:
            return format_error_response(CommonErrors.invalid_parameter(
                "congress", congress, congress_validation.error_message
            ))
    
    limit_validation = ParameterValidator.validate_limit_range(limit, 250)
    if not limit_validation.is_valid:
        return format_error_response(CommonErrors.invalid_parameter(
            "limit", limit, limit_validation.error_message
        ))
    
    if from_date:
        date_validation = ParameterValidator.validate_date_format(from_date)
        if not date_validation.is_valid:
            return format_error_response(CommonErrors.invalid_parameter(
                "from_date", from_date, date_validation.error_message
            ))
    
    if to_date:
        date_validation = ParameterValidator.validate_date_format(to_date)
        if not date_validation.is_valid:
            return format_error_response(CommonErrors.invalid_parameter(
                "to_date", to_date, date_validation.error_message
            ))
    
    params = {
        "format": "json",
        "limit": limit
    }
    
    if keywords:
        params["query"] = keywords
    if sort:
        params["sort"] = sort
    if from_date:
        params["fromDateTime"] = from_date
    if to_date:
        params["toDateTime"] = to_date
    
    # Determine endpoint based on congress parameter
    endpoint = BASE_ENDPOINT
    if congress:
        endpoint = f"{BASE_ENDPOINT}/{congress}"
    
    # Make safe API request
    data = await safe_congressional_request(endpoint, ctx, params)
    
    if "error" in data:
        logger.error(f"Error searching for nominations: {data['error']}")
        return format_error_response(CommonErrors.api_server_error(f"nominations search: {endpoint}", message=data['error']))
    
    if 'nominations' not in data:
        logger.warning("No nominations field in response")
        return "No nominations found matching the search criteria."
    
    nominations = data['nominations']
    if not nominations:
        logger.info("No nominations found matching the search criteria")
        return "No nominations found matching the search criteria."
    
    # Deduplicate results
    deduplicated_nominations = ResponseProcessor.deduplicate_results(
        nominations, 
        key_fields=['number', 'congress']
    )
    
    logger.info(f"Found {len(deduplicated_nominations)} unique nominations matching the search criteria")
    
    # Format the results
    lines = ["# Nominations Search Results"]
    lines.append("")
    for i, nomination in enumerate(deduplicated_nominations, 1):
        lines.append(f"## {i}. {format_nomination_item(nomination)}")
        lines.append("")
    
    return "\n".join(lines)
