import os
import re
from typing import Any

from fastmcp import FastMCP

from fathom_client import FathomClient

mcp = FastMCP(
    'Fathom',
    instructions='Access Fathom AI meeting data: list meetings, get transcripts and summaries, and browse teams and members.',
)

_client: FathomClient | None = None


def _get_client() -> FathomClient:
    global _client
    if _client is None:
        api_key = os.environ.get('FATHOM_API_KEY', '')
        if not api_key:
            raise ValueError('FATHOM_API_KEY environment variable is required')
        timeout = int(os.environ.get('FATHOM_TIMEOUT', '30'))
        _client = FathomClient(api_key, timeout=timeout)
    return _client


# -- Helpers --

def _ts_to_seconds(ts: str) -> int:
    """Convert HH:MM:SS timestamp to total seconds."""
    parts = ts.split(':')
    if len(parts) == 3:
        return int(parts[0]) * 3600 + int(parts[1]) * 60 + int(parts[2])
    if len(parts) == 2:
        return int(parts[0]) * 60 + int(parts[1])
    return 0


def _strip_md_links(text: str) -> str:
    """Strip markdown links, keeping only the link text: [text](url) -> text"""
    return re.sub(r'\[([^\]]+)\]\([^)]+\)', r'\1', text)


# -- Response formatters --

def _format_meeting(m: dict[str, Any]) -> str:
    recorded_by = (m.get('recorded_by') or {}).get('name', 'Unknown')
    invitees = ', '.join(
        f"{i.get('name')} <{i.get('email')}>"
        for i in m.get('calendar_invitees', [])
    )

    # Parse recording times to show just HH:MM
    start = (m.get('recording_start_time') or '')[:16].replace('T', ' ')
    end = (m.get('recording_end_time') or '')[:16].replace('T', ' ')
    date = (m.get('created_at') or '')[:10]

    lines = [
        f"[{m.get('recording_id')}] {m.get('title')}",
        f"  date: {date} | {start} - {end}",
        f"  url: {m.get('url')}",
        f"  by: {recorded_by}",
        f"  with: {invitees}",
    ]

    if m.get('default_summary'):
        md = m['default_summary'].get('markdown_formatted') or ''
        lines.append(f"  summary: {_strip_md_links(md)[:200]}...")

    if m.get('action_items'):
        items = []
        for a in m['action_items']:
            assignee = (a.get('assignee') or {}).get('name', '')
            status = 'done' if a.get('completed') else 'open'
            items.append(f"    - [{status}] {a.get('description')}" + (f" ({assignee})" if assignee else ''))
        lines.append('  action_items:')
        lines.extend(items)

    if m.get('crm_matches'):
        crm = m['crm_matches']
        if crm.get('error'):
            lines.append(f"  crm: {crm['error']}")
        else:
            parts = []
            for c in crm.get('contacts', []):
                parts.append(c.get('name', ''))
            for c in crm.get('companies', []):
                parts.append(c.get('name', ''))
            for d in crm.get('deals', []):
                parts.append(f"{d.get('name')} (${d.get('amount', 0):,})")
            if parts:
                lines.append(f"  crm: {', '.join(parts)}")

    return '\n'.join(lines)


def _format_transcript_items(items: list[dict]) -> str:
    lines = []
    for item in items:
        speaker = (item.get('speaker') or {}).get('display_name', 'Unknown')
        ts = item.get('timestamp', '')
        text = item.get('text', '')
        lines.append(f'{speaker} ({ts}): {text}')
    return '\n'.join(lines)


def _format_meetings_response(data: dict) -> str:
    meetings = [_format_meeting(m) for m in data.get('items', [])]
    out = '# Meetings\n\n' + '\n\n'.join(meetings)
    if data.get('next_cursor'):
        out += f"\n\n---\nnext_cursor: {data['next_cursor']}"
    return out


# -- Meetings --

@mcp.tool()
async def list_meetings(
    cursor: str | None = None,
    created_after: str | None = None,
    created_before: str | None = None,
    meeting_type: str | None = None,
    calendar_invitees_domains: list[str] | None = None,
    calendar_invitees_domains_type: str | None = None,
    recorded_by: list[str] | None = None,
    teams: list[str] | None = None,
    include_transcript: bool = False,
    include_summary: bool = False,
    include_action_items: bool = False,
    include_crm_matches: bool = False,
) -> str:
    """List meetings with optional filtering and pagination.

    Args:
        cursor: Pagination cursor from a previous response.
        created_after: ISO timestamp to filter meetings created after this date.
        created_before: ISO timestamp to filter meetings created before this date.
        meeting_type: Filter by 'internal' or 'external'.
        calendar_invitees_domains: Filter by invitee email domains (e.g. ["acme.com"]).
        calendar_invitees_domains_type: Filter invitee type: 'all', 'only_internal', or 'one_or_more_external'.
        recorded_by: Filter by recorder email addresses.
        teams: Filter by team names.
        include_transcript: Include transcript in each meeting.
        include_summary: Include summary in each meeting.
        include_action_items: Include action items in each meeting.
        include_crm_matches: Include CRM matches in each meeting.
    """
    client = _get_client()
    result = await client.list_meetings(
        cursor=cursor,
        created_after=created_after,
        created_before=created_before,
        meeting_type=meeting_type,
        calendar_invitees_domains=calendar_invitees_domains,
        calendar_invitees_domains_type=calendar_invitees_domains_type,
        recorded_by=recorded_by,
        teams=teams,
        include_transcript=include_transcript,
        include_summary=include_summary,
        include_action_items=include_action_items,
        include_crm_matches=include_crm_matches,
    )
    return _format_meetings_response(result)


# -- Recordings --

@mcp.tool()
async def get_transcript(
    recording_id: int,
    start_time: str | None = None,
    end_time: str | None = None,
) -> str:
    """Get the transcript for a meeting recording. Returns plaintext lines.

    For long meetings, use start_time/end_time to fetch a specific window.
    First call without time params to get metadata (duration, line count),
    then request specific chunks as needed.

    Args:
        recording_id: The recording ID (from list_meetings).
        start_time: Start of time window in HH:MM:SS format (e.g. "00:10:00").
        end_time: End of time window in HH:MM:SS format (e.g. "00:20:00").
    """
    client = _get_client()
    result = await client.get_transcript(recording_id)
    items = result.get('transcript', [])

    if not items:
        return 'No transcript available.'

    # Compute metadata from full transcript
    first_ts = items[0].get('timestamp', '00:00:00')
    last_ts = items[-1].get('timestamp', '00:00:00')
    total_lines = len(items)

    # Filter by time window if specified
    if start_time or end_time:
        start_sec = _ts_to_seconds(start_time) if start_time else 0
        end_sec = _ts_to_seconds(end_time) if end_time else float('inf')
        items = [
            item for item in items
            if start_sec <= _ts_to_seconds(item.get('timestamp', '00:00:00')) <= end_sec
        ]

    header = f'# Transcript (recording {recording_id})'
    header += f'\n# Duration: {first_ts} - {last_ts} | Total lines: {total_lines}'
    if start_time or end_time:
        header += f'\n# Showing: {start_time or first_ts} - {end_time or last_ts} ({len(items)} lines)'

    if not items:
        return header + '\n\nNo transcript lines in this time range.'

    return header + '\n\n' + _format_transcript_items(items)


@mcp.tool()
async def get_summary(recording_id: int) -> str:
    """Get the summary for a meeting recording. Returns clean markdown with playback URLs stripped.

    Args:
        recording_id: The recording ID (from list_meetings).
    """
    client = _get_client()
    result = await client.get_summary(recording_id)
    summary = result.get('summary', {})
    md = summary.get('markdown_formatted')
    if not md:
        return 'No summary available.'
    return _strip_md_links(md)


# -- Teams --

@mcp.tool()
async def list_teams(cursor: str | None = None) -> str:
    """List all teams.

    Args:
        cursor: Pagination cursor from a previous response.
    """
    client = _get_client()
    result = await client.list_teams(cursor=cursor)
    lines = [f"- {t['name']} (created {t['created_at'][:10]})" for t in result.get('items', [])]
    out = '# Teams\n\n' + '\n'.join(lines) if lines else 'No teams found.'
    if result.get('next_cursor'):
        out += f"\n\nnext_cursor: {result['next_cursor']}"
    return out


@mcp.tool()
async def list_team_members(
    team: str | None = None,
    cursor: str | None = None,
) -> str:
    """List team members, optionally filtered by team name.

    Args:
        team: Team name to filter by.
        cursor: Pagination cursor from a previous response.
    """
    client = _get_client()
    result = await client.list_team_members(team=team, cursor=cursor)
    lines = [f"- {m['name']} <{m['email']}>" for m in result.get('items', [])]
    out = '# Team Members\n\n' + '\n'.join(lines) if lines else 'No members found.'
    if result.get('next_cursor'):
        out += f"\n\nnext_cursor: {result['next_cursor']}"
    return out


def main():
    mcp.run()


if __name__ == '__main__':
    main()
