import {
  PUBLIC,
  USER_AUTH,
  USER_ERRORS,
  anyObj,
  arr,
  bool,
  created,
  err,
  int,
  jsonBody,
  limitParam,
  num,
  obj,
  objectId,
  ok,
  pageParam,
  paginated,
  pathParam,
  queryParam,
  ref,
  str,
} from './helpers';

export const TRACKER_TAGS = {
  auth: 'Tracker App - Auth',
  activity: 'Tracker App - Activity Logs',
  sync: 'Tracker App - Sync',
  updates: 'Tracker App - Updates & Downloads',
};

export const trackerTags = [
  {
    name: TRACKER_TAGS.auth,
    description:
      'The Tracker App signs in with the employee account (same endpoints as the Employee App). Use the returned access token as `UserBearerAuth`.',
  },
  {
    name: TRACKER_TAGS.activity,
    description:
      'Check-in/check-out, idle/active and break events that drive attendance. Timestamps are sent and returned as ' +
      '`yyyy-MM-dd HH:mm:ss` in Asia/Kolkata time. Open check-ins are auto-checked-out at 20:00 by a cron job.',
  },
  {
    name: TRACKER_TAGS.sync,
    description:
      'Bulk upload of the desktop app’s local SQLite data (sessions, tracking events, resource usage, periods). ' +
      'Stored in separate collections; records are upserted on employee + device + local id.',
  },
  { name: TRACKER_TAGS.updates, description: 'Version check and installer downloads (no auth required)' },
];

const T = TRACKER_TAGS;
const kolkataTime = (example: string, description = 'Asia/Kolkata local time, `yyyy-MM-dd HH:mm:ss`') => str(example, { description });

export const trackerPaths = {
  // ─── Activity logs ─────────────────────────────────────────────────────────
  '/api/desktop/session': {
    post: {
      tags: [T.activity],
      summary: 'Log a tracking event',
      description:
        'Records a single event. `CHECKIN`/`CHECKOUT` open and close a working session; `IDLE`/`ACTIVE` and ' +
        '`BREAK_START`/`BREAK_END` pairs are subtracted from working time in the day summaries.',
      security: USER_AUTH,
      requestBody: jsonBody(
        obj(
          {
            log_type: ref('DesktopLogType'),
            timestamp: kolkataTime('2026-09-30 09:31:05', 'Asia/Kolkata local time, `yyyy-MM-dd HH:mm:ss`. Defaults to now.'),
            ip_address: str('192.168.1.24'),
            os_info: ref('OsInfo'),
            device_name: str('EG-LAPTOP-12'),
          },
          ['log_type'],
        ),
      ),
      responses: {
        ...created(
          'Session logged',
          obj({
            _id: objectId(),
            log_type: ref('DesktopLogType'),
            timestamp: kolkataTime('2026-09-30 09:31:05'),
            createdAt: kolkataTime('2026-09-30 09:31:06'),
            ip_address: str('192.168.1.24'),
            os_info: ref('OsInfo'),
            device_name: str('EG-LAPTOP-12'),
            is_auto_checkout: bool(false, { description: 'Only present on CHECKOUT' }),
          }),
        ),
        ...err(400, 'Invalid log_type or timestamp format'),
        ...USER_ERRORS,
      },
    },
  },
  '/api/desktop/sessions': {
    get: {
      tags: [T.activity],
      summary: 'My raw tracking events',
      description: 'Newest first.',
      security: USER_AUTH,
      parameters: [pageParam, limitParam(50, 100)],
      responses: {
        ...paginated('Sessions fetched', obj({ count: int(50), sessions: arr(ref('DesktopSessionEvent')) })),
        ...USER_ERRORS,
      },
    },
  },
  '/api/desktop/day-summary': {
    get: {
      tags: [T.activity],
      summary: 'My per-day attendance summary',
      description:
        'One row per day (newest first) with check-in/out times and durations in **seconds**. ' +
        '`from_date` and `to_date` must be given together (inclusive, Asia/Kolkata days).',
      security: USER_AUTH,
      parameters: [
        pageParam,
        limitParam(10, 100),
        queryParam('from_date', { type: 'string', example: '2026-09-01', pattern: '^\\d{4}-\\d{2}-\\d{2}$' }, 'Start day, yyyy-MM-dd'),
        queryParam('to_date', { type: 'string', example: '2026-09-30', pattern: '^\\d{4}-\\d{2}-\\d{2}$' }, 'End day, yyyy-MM-dd'),
      ],
      responses: {
        ...paginated(
          'Day summary fetched',
          obj({
            count: int(10),
            days: arr(
              obj({
                date: str('30-09-2026', { description: 'dd-MM-yyyy' }),
                checkin: str('09:31', { description: 'HH:mm, or "-"' }),
                checkout: str('18:45', { description: 'HH:mm of the last CHECKOUT, or "-"' }),
                auto_checkout: bool(false),
                work: int(28745, { description: 'Seconds' }),
                break: int(3600, { description: 'Seconds' }),
                idle: int(900, { description: 'Seconds' }),
                interrupted: int(0, { description: 'Seconds' }),
                total: int(33245, { description: 'Seconds from first CHECKIN to last CHECKOUT' }),
                status: str('Completed', { enum: ['Completed', 'In Progress'] }),
              }),
            ),
          }),
        ),
        ...err(400, 'Only one of from_date/to_date given, invalid date, or from_date after to_date'),
        ...USER_ERRORS,
      },
    },
  },
  '/api/desktop/break-types': {
    get: {
      tags: [T.activity],
      summary: 'Selectable break types',
      security: USER_AUTH,
      responses: {
        ...ok('Break types fetched', obj({ break_types: { ...arr(str()), example: ['Training', 'Meeting', 'Lunch', 'Tea', 'Personal', 'Other'] } })),
        ...USER_ERRORS,
      },
    },
  },
  '/api/desktop/break': {
    post: {
      tags: [T.activity],
      summary: 'Log a typed break',
      description:
        'Logs a completed break against the current working session — the employee must be checked in. ' +
        'Use `TRACKING_INTERRUPTED` for gaps where the app was closed/offline; that time is deducted from working time.',
      security: USER_AUTH,
      requestBody: jsonBody(
        obj(
          {
            type: ref('BreakType'),
            start_time: kolkataTime('2026-09-30 13:00:00', 'Asia/Kolkata local time, `yyyy-MM-dd HH:mm:ss`. Defaults to now.'),
            end_time: kolkataTime('2026-09-30 13:30:00'),
            duration_seconds: num(1800, { minimum: 0, exclusiveMinimum: true }),
            ip_address: str('192.168.1.24'),
            os_info: ref('OsInfo'),
            device_name: str('EG-LAPTOP-12'),
          },
          ['type', 'end_time', 'duration_seconds'],
        ),
      ),
      responses: {
        ...created(
          'Break logged',
          obj({
            _id: objectId(),
            log_type: str('BREAK'),
            type: ref('BreakType'),
            start_time: kolkataTime('2026-09-30 13:00:00'),
            end_time: kolkataTime('2026-09-30 13:30:00'),
            duration_seconds: num(1800),
            createdAt: kolkataTime('2026-09-30 13:30:02'),
            ip_address: str('192.168.1.24'),
            os_info: ref('OsInfo'),
            device_name: str('EG-LAPTOP-12'),
          }),
        ),
        ...err(400, 'Invalid type/times/duration, end_time not after start_time, or not checked in'),
        ...USER_ERRORS,
      },
    },
  },

  // ─── Sync ──────────────────────────────────────────────────────────────────
  '/api/desktop/sync': {
    post: {
      tags: [T.sync],
      summary: 'Upload local tracking data',
      description:
        'All arrays are optional. Each record is upserted on (employee, device_name, `id`). Events, usage and periods are linked ' +
        'to a synced session through their `session_id` (the client-side session id). Returns the server id for every record.',
      security: USER_AUTH,
      requestBody: jsonBody(
        obj({
          sessions: arr(
            obj(
              {
                id: int(12, { description: 'Client-side (SQLite) id' }),
                login_time: str('2026-09-30T04:01:05.000Z', { format: 'date-time' }),
                logout_time: str(null, { format: 'date-time', nullable: true }),
                total_work_seconds: int(14400),
                total_break_seconds: int(1800),
                total_idle_seconds: int(600),
                status: str('active'),
                device_name: str('EG-LAPTOP-12'),
                operating_system: str('Windows 11 Pro'),
                ip_address: str('192.168.1.24'),
              },
              ['id', 'login_time'],
            ),
          ),
          tracking_events: arr(
            obj(
              {
                id: int(301),
                session_id: int(12, { nullable: true }),
                event_type: str('IDLE'),
                event_time: str('2026-09-30T06:10:00.000Z', { format: 'date-time' }),
                device_name: str('EG-LAPTOP-12'),
                operating_system: str('Windows 11 Pro'),
                ip_address: str('192.168.1.24'),
                metadata: anyObj(),
              },
              ['id', 'event_type', 'event_time'],
            ),
          ),
          resource_usage: arr(
            obj(
              {
                id: int(9001),
                session_id: int(12, { nullable: true }),
                cpu_usage_percent: num(23.5),
                ram_usage_mb: num(6120),
                ram_usage_percent: num(38.2),
                disk_read_speed_kbps: num(120),
                disk_write_speed_kbps: num(45),
                network_upload_speed_kbps: num(12),
                network_download_speed_kbps: num(340),
                total_memory_mb: num(16000),
                available_memory_mb: num(9880),
                device_name: str('EG-LAPTOP-12'),
                operating_system: str('Windows 11 Pro'),
                recorded_at: str('2026-09-30T06:10:00.000Z', { format: 'date-time' }),
              },
              ['id', 'recorded_at'],
            ),
          ),
          session_periods: arr(
            obj(
              {
                id: int(77),
                session_id: int(12, { nullable: true }),
                type: str('BREAK'),
                start_time: str('2026-09-30T07:30:00.000Z', { format: 'date-time' }),
                end_time: str('2026-09-30T08:00:00.000Z', { format: 'date-time', nullable: true }),
                duration_seconds: int(1800),
                duration_minutes: int(30),
                remarks: str('Lunch', { nullable: true }),
              },
              ['id', 'type', 'start_time'],
            ),
          ),
        }),
      ),
      responses: {
        ...ok(
          'Sync completed',
          obj({
            synced_sessions: arr(obj({ local_id: int(12), server_id: objectId() })),
            synced_tracking_events: arr(obj({ local_id: int(301), server_id: objectId() })),
            synced_resource_usage: arr(obj({ local_id: int(9001), server_id: objectId() })),
            synced_session_periods: arr(obj({ local_id: int(77), server_id: objectId() })),
          }),
        ),
        ...USER_ERRORS,
      },
    },
    get: {
      tags: [T.sync],
      summary: 'My synced data',
      description: 'Latest records from each sync collection.',
      security: USER_AUTH,
      parameters: [queryParam('limit', { type: 'integer', minimum: 1, maximum: 500, default: 100 }, 'Max records per collection')],
      responses: { ...ok('Sync data fetched', ref('SyncData')), ...USER_ERRORS },
    },
    patch: {
      tags: [T.sync],
      summary: 'Checkpoint an in-progress session',
      description: 'Updates running totals / logout time of a session that was already uploaded via `POST /api/desktop/sync`.',
      security: USER_AUTH,
      requestBody: jsonBody(
        obj(
          {
            session_id: int(12, { description: 'Client-side session id' }),
            logout_time: str('2026-09-30T13:15:00.000Z', { format: 'date-time', nullable: true }),
            total_work_seconds: int(30000),
            total_break_seconds: int(3600),
            total_idle_seconds: int(900),
            status: str('completed'),
          },
          ['session_id'],
        ),
      ),
      responses: {
        ...ok('Session updated', obj({ session: ref('SyncSession') })),
        ...err(400, 'Please provide session_id'),
        ...err(404, 'Session not found. Sync it first via POST /api/desktop/sync'),
        ...USER_ERRORS,
      },
    },
  },

  // ─── Updates & downloads ───────────────────────────────────────────────────
  '/api/desktop/latest-version': {
    get: {
      tags: [T.updates],
      summary: 'Latest active Tracker App version',
      description: 'Used by the app to check for updates. **Not** wrapped in the standard envelope.',
      security: PUBLIC,
      parameters: [queryParam('platform', { type: 'string', enum: ['windows', 'macos', 'linux'], default: 'windows' })],
      responses: {
        200: {
          description: 'Latest active release for the platform',
          content: {
            'application/json': {
              schema: obj({
                success: bool(true),
                platform: str('windows'),
                version: str('1.0.4'),
                mandatory: bool(false),
                release_notes: arr(str('Fixed idle detection')),
                download_url: str('https://tracker-api.eglogics.com/downloads/EGInTimeSetup_windows_v1.0.4.exe'),
              }),
            },
          },
        },
        400: {
          description: 'Invalid platform',
          content: { 'application/json': { schema: obj({ success: bool(false), message: str('platform must be one of windows, macos, linux') }) } },
        },
        404: {
          description: 'No active release for the platform',
          content: { 'application/json': { schema: obj({ success: bool(false), message: str('No active desktop release found for platform windows') }) } },
        },
      },
    },
  },
  '/downloads/{fileName}': {
    get: {
      tags: [T.updates],
      summary: 'Download an installer',
      description: 'Streams the installer file as an attachment. Use the `download_url` returned by `/api/desktop/latest-version`.',
      security: PUBLIC,
      parameters: [pathParam('fileName', 'Installer file name', { type: 'string', example: 'EGInTimeSetup_windows_v1.0.4.exe' })],
      responses: {
        200: { description: 'Installer file', content: { 'application/octet-stream': { schema: { type: 'string', format: 'binary' } } } },
        400: { description: 'Invalid file name', content: { 'application/json': { schema: obj({ success: bool(false), message: str('Invalid file name') }) } } },
        404: { description: 'File not found', content: { 'application/json': { schema: obj({ success: bool(false), message: str('File not found') }) } } },
      },
    },
  },
};
