import { db } from '../db/client.js';
import type { BirdGender, BirdRow, PendingBirdTransferRow, VetVisitRow, WeightRow } from '../types.js';

const birdSelectFields = `
  birds.id,
  birds.workspace_id,
  birds.name,
  birds.tag_id,
  birds.species,
  birds.gender,
  birds.date_of_birth::text,
  birds.gotcha_day::text,
  birds.chart_color,
  birds.photo_data_url,
  birds.notify_on_dob,
  birds.notify_on_gotcha_day,
  birds.created_at,
  latest.weight_grams AS latest_weight_grams,
  latest.recorded_on::text AS latest_recorded_on
`;

export const getBirdById = async (birdId: string, workspaceId: number) => {
  const result = await db.query<BirdRow>(
    `SELECT
      ${birdSelectFields}
    FROM birds
    LEFT JOIN LATERAL (
      SELECT weight_grams, recorded_on
      FROM weight_records
      WHERE weight_records.bird_id = birds.id
      ORDER BY recorded_on DESC
      LIMIT 1
    ) latest ON TRUE
    WHERE birds.id = $1
      AND birds.workspace_id = $2`,
    [birdId, workspaceId],
  );

  return result.rows[0] ?? null;
};

export const listBirds = async (workspaceId: number) => {
  const result = await db.query<BirdRow>(
    `SELECT
      ${birdSelectFields}
    FROM birds
    LEFT JOIN LATERAL (
      SELECT weight_grams, recorded_on
      FROM weight_records
      WHERE weight_records.bird_id = birds.id
      ORDER BY recorded_on DESC
      LIMIT 1
    ) latest ON TRUE
    WHERE birds.workspace_id = $1
    ORDER BY birds.name ASC`,
    [workspaceId],
  );

  return result.rows;
};

export const createBird = async ({
  workspaceId,
  name,
  tagId,
  species,
  gender,
  dateOfBirth,
  gotchaDay,
  chartColor,
  photoDataUrl,
  notifyOnDob,
  notifyOnGotchaDay,
}: {
  workspaceId: number;
  name: string;
  tagId: string;
  species: string;
  gender: BirdGender;
  dateOfBirth: string | null;
  gotchaDay: string | null;
  chartColor: string;
  photoDataUrl: string | null;
  notifyOnDob: boolean;
  notifyOnGotchaDay: boolean;
}) => {
  const result = await db.query<BirdRow>(
    `INSERT INTO birds (workspace_id, name, tag_id, species, gender, date_of_birth, gotcha_day, chart_color, photo_data_url, notify_on_dob, notify_on_gotcha_day)
     VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)
     RETURNING id, workspace_id, name, tag_id, species, gender, date_of_birth::text, gotcha_day::text, chart_color, photo_data_url, notify_on_dob, notify_on_gotcha_day, created_at, NULL::text AS latest_weight_grams, NULL::text AS latest_recorded_on`,
    [workspaceId, name, tagId, species, gender, dateOfBirth, gotchaDay, chartColor, photoDataUrl, notifyOnDob, notifyOnGotchaDay],
  );

  return result.rows[0] ?? null;
};

export const updateBird = async ({
  birdId,
  workspaceId,
  name,
  tagId,
  species,
  gender,
  dateOfBirth,
  gotchaDay,
  chartColor,
  photoDataUrl,
  notifyOnDob,
  notifyOnGotchaDay,
}: {
  birdId: string;
  workspaceId: number;
  name: string;
  tagId: string;
  species: string;
  gender: BirdGender;
  dateOfBirth: string | null;
  gotchaDay: string | null;
  chartColor: string;
  photoDataUrl: string | null;
  notifyOnDob: boolean;
  notifyOnGotchaDay: boolean;
}) => {
  const result = await db.query<BirdRow>(
    `UPDATE birds
     SET name = $2,
         tag_id = $3,
         species = $4,
         gender = $5,
         date_of_birth = $6,
         gotcha_day = $7,
         chart_color = $8,
         photo_data_url = $9,
         notify_on_dob = $10,
         notify_on_gotcha_day = $11
     WHERE id = $1
       AND workspace_id = $12
     RETURNING id, workspace_id, name, tag_id, species, gender, date_of_birth::text, gotcha_day::text, chart_color, photo_data_url, notify_on_dob, notify_on_gotcha_day, created_at,
       (
         SELECT weight_grams::text
         FROM weight_records
         WHERE bird_id = birds.id
         ORDER BY recorded_on DESC
         LIMIT 1
       ) AS latest_weight_grams,
       (
         SELECT recorded_on::text
         FROM weight_records
         WHERE bird_id = birds.id
         ORDER BY recorded_on DESC
         LIMIT 1
       ) AS latest_recorded_on`,
    [birdId, name, tagId, species, gender, dateOfBirth, gotchaDay, chartColor, photoDataUrl, notifyOnDob, notifyOnGotchaDay, workspaceId],
  );

  return result.rows[0] ?? null;
};

export const deleteBird = async (birdId: string, workspaceId: number) => {
  const result = await db.query<{ id: string }>(
    `DELETE FROM birds
     WHERE id = $1
       AND workspace_id = $2
     RETURNING id`,
    [birdId, workspaceId],
  );

  return Boolean(result.rowCount);
};

export const transferBirdToWorkspace = async (birdId: string, sourceWorkspaceId: number, targetWorkspaceId: number) => {
  const result = await db.query<BirdRow>(
    `UPDATE birds
     SET workspace_id = $3
     WHERE id = $1
       AND workspace_id = $2
     RETURNING id, workspace_id, name, tag_id, species, gender, date_of_birth::text, gotcha_day::text, chart_color, photo_data_url, notify_on_dob, notify_on_gotcha_day, created_at,
       (
         SELECT weight_grams::text
         FROM weight_records
         WHERE bird_id = birds.id
         ORDER BY recorded_on DESC
         LIMIT 1
       ) AS latest_weight_grams,
       (
         SELECT recorded_on::text
         FROM weight_records
         WHERE bird_id = birds.id
         ORDER BY recorded_on DESC
         LIMIT 1
       ) AS latest_recorded_on`,
    [birdId, sourceWorkspaceId, targetWorkspaceId],
  );

  return result.rows[0] ?? null;
};

export const createPendingBirdTransfer = async ({
  birdId,
  sourceWorkspaceId,
  destinationOwnerEmail,
  requestedByUserId,
}: {
  birdId: string;
  sourceWorkspaceId: number;
  destinationOwnerEmail: string;
  requestedByUserId: string;
}) => {
  const result = await db.query<PendingBirdTransferRow>(
    `INSERT INTO pending_bird_transfers (bird_id, source_workspace_id, destination_owner_email, requested_by_user_id)
     VALUES ($1, $2, $3, $4)
     ON CONFLICT (bird_id) WHERE completed_at IS NULL DO UPDATE
     SET destination_owner_email = EXCLUDED.destination_owner_email,
         requested_by_user_id = EXCLUDED.requested_by_user_id,
         last_error = NULL,
         created_at = CURRENT_TIMESTAMP
     RETURNING id, bird_id, source_workspace_id, destination_owner_email, requested_by_user_id, completed_at::text, completed_workspace_id, last_error, created_at`,
    [birdId, sourceWorkspaceId, destinationOwnerEmail, requestedByUserId],
  );

  return result.rows[0] ?? null;
};

export const listPendingBirdTransfersForOwnerEmail = async (ownerEmail: string) => {
  const result = await db.query<PendingBirdTransferRow>(
    `SELECT id, bird_id, source_workspace_id, destination_owner_email, requested_by_user_id, completed_at::text, completed_workspace_id, last_error, created_at
     FROM pending_bird_transfers
     WHERE LOWER(destination_owner_email) = LOWER($1)
       AND completed_at IS NULL
     ORDER BY created_at ASC`,
    [ownerEmail],
  );

  return result.rows;
};

export const markPendingBirdTransferCompleted = async (transferId: string, completedWorkspaceId: number) => {
  await db.query(
    `UPDATE pending_bird_transfers
     SET completed_at = CURRENT_TIMESTAMP,
         completed_workspace_id = $2,
         last_error = NULL
     WHERE id = $1`,
    [transferId, completedWorkspaceId],
  );
};

export const markPendingBirdTransferFailed = async (transferId: string, lastError: string) => {
  await db.query(
    `UPDATE pending_bird_transfers
     SET last_error = $2
     WHERE id = $1`,
    [transferId, lastError],
  );
};

export const completePendingBirdTransfersForOwner = async (ownerEmail: string, targetWorkspaceId: number) => {
  const transfers = await listPendingBirdTransfersForOwnerEmail(ownerEmail);
  let completed = 0;
  let failed = 0;

  for (const transfer of transfers) {
    try {
      const bird = await transferBirdToWorkspace(transfer.bird_id, transfer.source_workspace_id, targetWorkspaceId);

      if (!bird) {
        failed += 1;
        await markPendingBirdTransferFailed(transfer.id, 'Bird is no longer available in the source flock.');
        continue;
      }

      await markPendingBirdTransferCompleted(transfer.id, targetWorkspaceId);
      completed += 1;
    } catch (error) {
      failed += 1;
      const message =
        typeof error === 'object' && error && 'code' in error && error.code === '23505'
          ? 'The receiving flock already has a bird using the same band/tag ID.'
          : error instanceof Error
            ? error.message
            : 'Unable to complete pending bird transfer.';
      await markPendingBirdTransferFailed(transfer.id, message);
    }
  }

  return { completed, failed };
};

export const listWeightsForBird = async (birdId: string, workspaceId: number, days: number) => {
  const result = await db.query<WeightRow>(
    `SELECT id, bird_id, weight_grams, recorded_on::text, notes
     FROM weight_records
     WHERE bird_id = $1
       AND EXISTS (
         SELECT 1
         FROM birds
         WHERE birds.id = weight_records.bird_id
           AND birds.workspace_id = $3
       )
       AND recorded_on >= CURRENT_DATE - (($2::int - 1) * INTERVAL '1 day')
     ORDER BY recorded_on ASC`,
    [birdId, days, workspaceId],
  );

  return result.rows;
};

export const createWeightForBird = async (birdId: string, weightGrams: number, recordedOn: string, notes: string | null) => {
  const result = await db.query<WeightRow>(
    `INSERT INTO weight_records (bird_id, weight_grams, recorded_on, notes)
     VALUES ($1, $2, $3, $4)
     RETURNING id, bird_id, weight_grams, recorded_on::text, notes`,
    [birdId, weightGrams, recordedOn, notes],
  );

  return result.rows[0] ?? null;
};

export const listVetVisitsForBird = async (birdId: string, workspaceId: number) => {
  const result = await db.query<VetVisitRow>(
    `SELECT id, bird_id, visited_on::text, clinic_name, reason, notes
     FROM vet_visits
     WHERE bird_id = $1
       AND EXISTS (
         SELECT 1
         FROM birds
         WHERE birds.id = vet_visits.bird_id
           AND birds.workspace_id = $2
       )
     ORDER BY visited_on DESC, created_at DESC`,
    [birdId, workspaceId],
  );

  return result.rows;
};

export const createVetVisitForBird = async (birdId: string, visitedOn: string, clinicName: string, reason: string, notes: string | null) => {
  const result = await db.query<VetVisitRow>(
    `INSERT INTO vet_visits (bird_id, visited_on, clinic_name, reason, notes)
     VALUES ($1, $2, $3, $4, $5)
     RETURNING id, bird_id, visited_on::text, clinic_name, reason, notes`,
    [birdId, visitedOn, clinicName, reason, notes],
  );

  return result.rows[0] ?? null;
};

export const updateVetVisitForBird = async (
  visitId: string,
  birdId: string,
  visitedOn: string,
  clinicName: string,
  reason: string,
  notes: string | null,
) => {
  const result = await db.query<VetVisitRow>(
    `UPDATE vet_visits
     SET visited_on = $3,
         clinic_name = $4,
         reason = $5,
         notes = $6
     WHERE id = $1
       AND bird_id = $2
     RETURNING id, bird_id, visited_on::text, clinic_name, reason, notes`,
    [visitId, birdId, visitedOn, clinicName, reason, notes],
  );

  return result.rows[0] ?? null;
};

export const deleteVetVisitForBird = async (visitId: string, birdId: string) => {
  const result = await db.query<{ id: string }>(
    `DELETE FROM vet_visits
     WHERE id = $1
       AND bird_id = $2
     RETURNING id`,
    [visitId, birdId],
  );

  return (result.rowCount ?? 0) > 0;
};
