import { McpServer } from '@modelcontextprotocol/sdk/server/mcp';
import { z } from 'zod';
import { canAccessTrip, db } from '../../db/database';
import { isDemoUser } from '../../services/authService';
import {
  getDay, updateDay, validateAccommodationRefs,
  createDay, deleteDay,
  createAccommodation, getAccommodation, updateAccommodation, deleteAccommodation,
} from '../../services/dayService';
import { createPlace } from '../../services/placeService';
import {
  createNote as createDayNote, getNote as getDayNote, updateNote as updateDayNote,
  deleteNote as deleteDayNote, dayExists as dayNoteExists,
} from '../../services/dayNoteService';
import {
  safeBroadcast, TOOL_ANNOTATIONS_WRITE, TOOL_ANNOTATIONS_DELETE,
  TOOL_ANNOTATIONS_NON_IDEMPOTENT,
  demoDenied, noAccess, ok,
} from './_shared';
import { canWrite } from '../scopes';

export function registerDayTools(server: McpServer, userId: number, scopes: string[] | null): void {
  if (!canWrite(scopes, 'trips')) return;

  // --- DAYS ---

  server.registerTool(
    'update_day',
    {
      description: 'Set the title of a day in a trip (e.g. "Arrival in Paris", "Free day").',
      inputSchema: {
        tripId: z.number().int().positive(),
        dayId: z.number().int().positive(),
        title: z.string().max(200).nullable().describe('Day title, or null to clear it'),
      },
      annotations: TOOL_ANNOTATIONS_WRITE,
    },
    async ({ tripId, dayId, title }) => {
      if (isDemoUser(userId)) return demoDenied();
      if (!canAccessTrip(tripId, userId)) return noAccess();
      const current = getDay(dayId, tripId);
      if (!current) return { content: [{ type: 'text' as const, text: 'Day not found.' }], isError: true };
      const updated = updateDay(dayId, current, title !== undefined ? { title } : {});
      safeBroadcast(tripId, 'day:updated', { day: updated });
      return ok({ day: updated });
    }
  );

  server.registerTool(
    'create_day',
    {
      description: 'Add a new day to a trip (optionally with a specific date and notes).',
      inputSchema: {
        tripId: z.number().int().positive(),
        date: z.string().optional().describe('ISO date string YYYY-MM-DD, optional for dateless trips'),
        notes: z.string().optional(),
      },
      annotations: TOOL_ANNOTATIONS_NON_IDEMPOTENT,
    },
    async ({ tripId, date, notes }) => {
      if (isDemoUser(userId)) return demoDenied();
      if (!canAccessTrip(tripId, userId)) return noAccess();
      const day = createDay(tripId, date, notes);
      safeBroadcast(tripId, 'day:created', { day });
      return ok({ day });
    }
  );

  server.registerTool(
    'delete_day',
    {
      description: 'Delete a day from a trip.',
      inputSchema: {
        tripId: z.number().int().positive(),
        dayId: z.number().int().positive(),
      },
      annotations: TOOL_ANNOTATIONS_DELETE,
    },
    async ({ tripId, dayId }) => {
      if (isDemoUser(userId)) return demoDenied();
      if (!canAccessTrip(tripId, userId)) return noAccess();
      if (!getDay(dayId, tripId)) return { content: [{ type: 'text' as const, text: 'Day not found.' }], isError: true };
      deleteDay(dayId);
      safeBroadcast(tripId, 'day:deleted', { id: dayId });
      return ok({ success: true });
    }
  );

  server.registerTool(
    'create_accommodation',
    {
      description: 'Add an accommodation (hotel, Airbnb, etc.) to a trip, linked to a place and a date range.',
      inputSchema: {
        tripId: z.number().int().positive(),
        place_id: z.number().int().positive().describe('The place to use as the accommodation'),
        start_day_id: z.number().int().positive().describe('Check-in day ID'),
        end_day_id: z.number().int().positive().describe('Check-out day ID'),
        check_in: z.string().max(10).optional().describe('Check-in time e.g. "15:00"'),
        check_out: z.string().max(10).optional().describe('Check-out time e.g. "11:00"'),
        confirmation: z.string().max(100).optional(),
        notes: z.string().max(1000).optional(),
      },
      annotations: TOOL_ANNOTATIONS_NON_IDEMPOTENT,
    },
    async ({ tripId, place_id, start_day_id, end_day_id, check_in, check_out, confirmation, notes }) => {
      if (isDemoUser(userId)) return demoDenied();
      if (!canAccessTrip(tripId, userId)) return noAccess();
      const errors = validateAccommodationRefs(tripId, place_id, start_day_id, end_day_id);
      if (errors.length > 0) return { content: [{ type: 'text' as const, text: errors.map(e => e.message).join(', ') }], isError: true };
      const accommodation = createAccommodation(tripId, { place_id, start_day_id, end_day_id, check_in, check_out, confirmation, notes });
      safeBroadcast(tripId, 'accommodation:created', { accommodation });
      return ok({ accommodation });
    }
  );

  server.registerTool(
    'create_place_accommodation',
    {
      description: 'Create a new place and immediately set it as an accommodation for a date range in one atomic operation. Use place details from search_place results. Only use when the place does not yet exist — if it already exists, use create_accommodation directly.',
      inputSchema: {
        tripId: z.number().int().positive(),
        name: z.string().min(1).max(200),
        description: z.string().max(2000).optional(),
        lat: z.number().optional(),
        lng: z.number().optional(),
        address: z.string().max(500).optional(),
        category_id: z.number().int().positive().optional().describe('Category ID — use list_categories to see available options'),
        google_place_id: z.string().optional().describe('Google Place ID from search_place — enables opening hours display'),
        osm_id: z.string().optional().describe('OpenStreetMap ID from search_place (e.g. "way:12345")'),
        place_notes: z.string().max(2000).optional().describe('Notes for the place'),
        website: z.string().max(500).optional(),
        phone: z.string().max(50).optional(),
        start_day_id: z.number().int().positive().describe('Check-in day ID'),
        end_day_id: z.number().int().positive().describe('Check-out day ID'),
        check_in: z.string().max(10).optional().describe('Check-in time e.g. "15:00"'),
        check_out: z.string().max(10).optional().describe('Check-out time e.g. "11:00"'),
        confirmation: z.string().max(100).optional(),
        accommodation_notes: z.string().max(1000).optional().describe('Notes for the accommodation'),
      },
      annotations: TOOL_ANNOTATIONS_NON_IDEMPOTENT,
    },
    async ({ tripId, name, description, lat, lng, address, category_id, google_place_id, osm_id, place_notes, website, phone, start_day_id, end_day_id, check_in, check_out, confirmation, accommodation_notes }) => {
      if (isDemoUser(userId)) return demoDenied();
      if (!canAccessTrip(tripId, userId)) return noAccess();
      const dayErrors = validateAccommodationRefs(tripId, undefined, start_day_id, end_day_id);
      if (dayErrors.length > 0) return { content: [{ type: 'text' as const, text: dayErrors.map(e => e.message).join(', ') }], isError: true };
      try {
        const run = db.transaction(() => {
          const place = createPlace(String(tripId), { name, description, lat, lng, address, category_id, google_place_id, osm_id, notes: place_notes, website, phone });
          const accommodation = createAccommodation(tripId, { place_id: place.id, start_day_id, end_day_id, check_in, check_out, confirmation, notes: accommodation_notes });
          return { place, accommodation };
        });
        const result = run();
        safeBroadcast(tripId, 'place:created', { place: result.place });
        safeBroadcast(tripId, 'accommodation:created', { accommodation: result.accommodation });
        return ok(result);
      } catch {
        return { content: [{ type: 'text' as const, text: 'Failed to create place and accommodation.' }], isError: true };
      }
    }
  );

  server.registerTool(
    'update_accommodation',
    {
      description: 'Update fields on an existing accommodation.',
      inputSchema: {
        tripId: z.number().int().positive(),
        accommodationId: z.number().int().positive(),
        place_id: z.number().int().positive().optional(),
        start_day_id: z.number().int().positive().optional(),
        end_day_id: z.number().int().positive().optional(),
        check_in: z.string().max(10).optional(),
        check_out: z.string().max(10).optional(),
        confirmation: z.string().max(100).optional(),
        notes: z.string().max(1000).optional(),
      },
      annotations: TOOL_ANNOTATIONS_WRITE,
    },
    async ({ tripId, accommodationId, place_id, start_day_id, end_day_id, check_in, check_out, confirmation, notes }) => {
      if (isDemoUser(userId)) return demoDenied();
      if (!canAccessTrip(tripId, userId)) return noAccess();
      const existing = getAccommodation(accommodationId, tripId);
      if (!existing) return { content: [{ type: 'text' as const, text: 'Accommodation not found.' }], isError: true };
      const accommodation = updateAccommodation(accommodationId, existing, { place_id, start_day_id, end_day_id, check_in, check_out, confirmation, notes });
      safeBroadcast(tripId, 'accommodation:updated', { accommodation });
      return ok({ accommodation });
    }
  );

  server.registerTool(
    'delete_accommodation',
    {
      description: 'Delete an accommodation from a trip.',
      inputSchema: {
        tripId: z.number().int().positive(),
        accommodationId: z.number().int().positive(),
      },
      annotations: TOOL_ANNOTATIONS_DELETE,
    },
    async ({ tripId, accommodationId }) => {
      if (isDemoUser(userId)) return demoDenied();
      if (!canAccessTrip(tripId, userId)) return noAccess();
      if (!getAccommodation(accommodationId, tripId)) return { content: [{ type: 'text' as const, text: 'Accommodation not found.' }], isError: true };
      const { linkedReservationId } = deleteAccommodation(accommodationId);
      safeBroadcast(tripId, 'accommodation:deleted', { id: accommodationId, linkedReservationId });
      return ok({ success: true, linkedReservationId });
    }
  );

  // --- DAY NOTES ---

  server.registerTool(
    'create_day_note',
    {
      description: 'Add a note to a specific day in a trip.',
      inputSchema: {
        tripId: z.number().int().positive(),
        dayId: z.number().int().positive(),
        text: z.string().min(1).max(500),
        time: z.string().max(150).optional().describe('Time label (e.g. "09:00" or "Morning")'),
        icon: z.string().optional().describe('Emoji icon for the note'),
      },
      annotations: TOOL_ANNOTATIONS_NON_IDEMPOTENT,
    },
    async ({ tripId, dayId, text, time, icon }) => {
      if (isDemoUser(userId)) return demoDenied();
      if (!canAccessTrip(tripId, userId)) return noAccess();
      if (!dayNoteExists(dayId, tripId)) return { content: [{ type: 'text' as const, text: 'Day not found.' }], isError: true };
      const note = createDayNote(dayId, tripId, text, time, icon);
      safeBroadcast(tripId, 'dayNote:created', { dayId, note });
      return ok({ note });
    }
  );

  server.registerTool(
    'update_day_note',
    {
      description: 'Edit an existing note on a specific day.',
      inputSchema: {
        tripId: z.number().int().positive(),
        dayId: z.number().int().positive(),
        noteId: z.number().int().positive(),
        text: z.string().min(1).max(500).optional(),
        time: z.string().max(150).nullable().optional().describe('Time label (e.g. "09:00" or "Morning"), or null to clear'),
        icon: z.string().optional().describe('Emoji icon for the note'),
      },
      annotations: TOOL_ANNOTATIONS_WRITE,
    },
    async ({ tripId, dayId, noteId, text, time, icon }) => {
      if (isDemoUser(userId)) return demoDenied();
      if (!canAccessTrip(tripId, userId)) return noAccess();
      const existing = getDayNote(noteId, dayId, tripId);
      if (!existing) return { content: [{ type: 'text' as const, text: 'Note not found.' }], isError: true };
      const note = updateDayNote(noteId, existing, { text, time: time !== undefined ? time : undefined, icon });
      safeBroadcast(tripId, 'dayNote:updated', { dayId, note });
      return ok({ note });
    }
  );

  server.registerTool(
    'delete_day_note',
    {
      description: 'Delete a note from a specific day.',
      inputSchema: {
        tripId: z.number().int().positive(),
        dayId: z.number().int().positive(),
        noteId: z.number().int().positive(),
      },
      annotations: TOOL_ANNOTATIONS_DELETE,
    },
    async ({ tripId, dayId, noteId }) => {
      if (isDemoUser(userId)) return demoDenied();
      if (!canAccessTrip(tripId, userId)) return noAccess();
      const note = getDayNote(noteId, dayId, tripId);
      if (!note) return { content: [{ type: 'text' as const, text: 'Note not found.' }], isError: true };
      deleteDayNote(noteId);
      safeBroadcast(tripId, 'dayNote:deleted', { noteId, dayId });
      return ok({ success: true });
    }
  );
}
