/**
 * Tests for 404 handling in message delete operations.
 *
 * When messages in Trash/Junk are already deleted server-side (e.g. by another
 * client, server cleanup, or a previous successful delete whose acknowledgment
 * was lost), the DELETE API returns 404.  These tests verify that 404 responses
 * are treated as successful deletes rather than failures, preventing the
 * "Failed to delete N messages" toast that users were seeing.
 */

// ---------------------------------------------------------------------------
// Helpers: extract the is404 detection logic from mailboxStore so we can
// unit-test it in isolation without needing the full Svelte store wiring.
// ---------------------------------------------------------------------------

/**
 * Determines whether a rejection reason from a DELETE /v1/messages/:id call
 * represents a "message already gone" condition that should be treated as
 * a successful delete.
 *
 * This mirrors the logic added to:
 *   - deleteMessage (single)
 *   - bulkDeleteMessages
 *   - emptyFolder (used by emptyTrash / emptySpam)
 *
 * @param {Error|object|null} reason - The rejection reason from Promise.allSettled or catch
 * @returns {boolean}
 */
function is404OrNotFound(reason) {
  if (!reason) return false;
  const status = reason?.status;
  const message = reason?.message || '';
  return status === 404 || /not (found|exist)/i.test(message);
}

describe('is404OrNotFound detection', () => {
  it('detects a 404 status error', () => {
    const err = new Error('Not Found');
    err.status = 404;
    expect(is404OrNotFound(err)).toBe(true);
  });

  it('detects "does not exist" message without status', () => {
    const err = new Error('Message does not exist');
    expect(is404OrNotFound(err)).toBe(true);
  });

  it('detects "not found" message without status', () => {
    const err = new Error('Message not found');
    expect(is404OrNotFound(err)).toBe(true);
  });

  it('detects "Not Found" (capitalized) message', () => {
    const err = new Error('Not Found');
    expect(is404OrNotFound(err)).toBe(true);
  });

  it('does not match a 500 server error', () => {
    const err = new Error('Internal Server Error');
    err.status = 500;
    expect(is404OrNotFound(err)).toBe(false);
  });

  it('does not match a 401 auth error', () => {
    const err = new Error('Unauthorized');
    err.status = 401;
    expect(is404OrNotFound(err)).toBe(false);
  });

  it('does not match a network timeout', () => {
    const err = new Error('Request timeout');
    err.status = 408;
    expect(is404OrNotFound(err)).toBe(false);
  });

  it('does not match a generic error', () => {
    const err = new Error('Something went wrong');
    expect(is404OrNotFound(err)).toBe(false);
  });

  it('handles null/undefined reason gracefully', () => {
    expect(is404OrNotFound(null)).toBe(false);
    expect(is404OrNotFound(undefined)).toBe(false);
  });

  it('handles plain object with status 404', () => {
    expect(is404OrNotFound({ status: 404, message: 'Not Found' })).toBe(true);
  });

  it('handles plain object with "not exist" message (natural language)', () => {
    expect(is404OrNotFound({ message: 'Message does not exist' })).toBe(true);
  });

  it('does not match underscore-delimited error codes without status', () => {
    // The server error code MESSAGE_DOES_NOT_EXIST will always come with
    // status: 404, so the status check catches it. The regex only needs
    // to handle natural-language messages.
    expect(is404OrNotFound({ message: 'MESSAGE_DOES_NOT_EXIST' })).toBe(false);
    // But with status 404 it matches:
    expect(is404OrNotFound({ status: 404, message: 'MESSAGE_DOES_NOT_EXIST' })).toBe(true);
  });
});

// ---------------------------------------------------------------------------
// Integration-style tests: simulate bulkDeleteMessages result processing
// ---------------------------------------------------------------------------

describe('bulkDeleteMessages 404 handling', () => {
  /**
   * Simulates the result-processing loop from bulkDeleteMessages
   * with the 404-as-success fix applied.
   */
  function processChunkResults(chunkResults, chunk) {
    const results = [];
    const successfulDeletes = [];

    chunkResults.forEach((result, idx) => {
      if (result.status === 'fulfilled') {
        successfulDeletes.push(chunk[idx]);
        results.push(true);
      } else {
        const reason = result.reason;
        const is404 = reason?.status === 404 || /not (found|exist)/i.test(reason?.message || '');
        if (is404) {
          successfulDeletes.push(chunk[idx]);
          results.push(true);
        } else {
          results.push(false);
        }
      }
    });

    return {
      success: results.filter(Boolean).length,
      failed: results.length - results.filter(Boolean).length,
      successfulDeletes,
    };
  }

  it('counts all fulfilled deletes as success', () => {
    const chunk = [{ id: '1' }, { id: '2' }, { id: '3' }];
    const chunkResults = [
      { status: 'fulfilled', value: chunk[0] },
      { status: 'fulfilled', value: chunk[1] },
      { status: 'fulfilled', value: chunk[2] },
    ];

    const { success, failed, successfulDeletes } = processChunkResults(chunkResults, chunk);
    expect(success).toBe(3);
    expect(failed).toBe(0);
    expect(successfulDeletes).toHaveLength(3);
  });

  it('counts 404 rejections as success (not failure)', () => {
    const chunk = [{ id: '1' }, { id: '2' }, { id: '3' }];
    const err404 = new Error('Not Found');
    err404.status = 404;

    const chunkResults = [
      { status: 'fulfilled', value: chunk[0] },
      { status: 'rejected', reason: err404 },
      { status: 'fulfilled', value: chunk[2] },
    ];

    const { success, failed, successfulDeletes } = processChunkResults(chunkResults, chunk);
    expect(success).toBe(3);
    expect(failed).toBe(0);
    expect(successfulDeletes).toHaveLength(3);
  });

  it('counts "does not exist" rejections as success', () => {
    const chunk = [{ id: '1' }, { id: '2' }];
    const errNotExist = new Error('Message does not exist');

    const chunkResults = [
      { status: 'rejected', reason: errNotExist },
      { status: 'rejected', reason: errNotExist },
    ];

    const { success, failed } = processChunkResults(chunkResults, chunk);
    expect(success).toBe(2);
    expect(failed).toBe(0);
  });

  it('still counts real errors as failures', () => {
    const chunk = [{ id: '1' }, { id: '2' }, { id: '3' }];
    const err500 = new Error('Internal Server Error');
    err500.status = 500;
    const err404 = new Error('Not Found');
    err404.status = 404;

    const chunkResults = [
      { status: 'fulfilled', value: chunk[0] },
      { status: 'rejected', reason: err500 },
      { status: 'rejected', reason: err404 },
    ];

    const { success, failed } = processChunkResults(chunkResults, chunk);
    expect(success).toBe(2); // fulfilled + 404
    expect(failed).toBe(1); // 500
  });

  it('handles mixed 404 and timeout errors correctly', () => {
    const chunk = [{ id: '1' }, { id: '2' }, { id: '3' }, { id: '4' }];
    const err404 = new Error('Not Found');
    err404.status = 404;
    const errTimeout = new Error('Request timeout');
    errTimeout.status = 408;

    const chunkResults = [
      { status: 'fulfilled', value: chunk[0] },
      { status: 'rejected', reason: err404 },
      { status: 'rejected', reason: errTimeout },
      { status: 'rejected', reason: err404 },
    ];

    const { success, failed } = processChunkResults(chunkResults, chunk);
    expect(success).toBe(3); // fulfilled + two 404s
    expect(failed).toBe(1); // timeout
  });

  it('handles all messages being 404 (entire Trash already cleaned)', () => {
    const chunk = [{ id: '1' }, { id: '2' }, { id: '3' }];
    const err404 = new Error('MESSAGE_DOES_NOT_EXIST');
    err404.status = 404;

    const chunkResults = chunk.map(() => ({
      status: 'rejected',
      reason: err404,
    }));

    const { success, failed } = processChunkResults(chunkResults, chunk);
    expect(success).toBe(3);
    expect(failed).toBe(0);
  });
});

// ---------------------------------------------------------------------------
// Integration-style tests: simulate emptyFolder result processing
// ---------------------------------------------------------------------------

describe('emptyFolder 404 handling', () => {
  /**
   * Simulates the result-processing loop from emptyFolder
   * with the 404-as-success fix applied.
   */
  function processEmptyFolderResults(results) {
    let successCount = 0;
    let failCount = 0;

    results.forEach((result) => {
      if (result.status === 'fulfilled') {
        successCount++;
      } else {
        const reason = result.reason;
        const is404 = reason?.status === 404 || /not (found|exist)/i.test(reason?.message || '');
        if (is404) {
          successCount++;
        } else {
          failCount++;
        }
      }
    });

    return { successCount, failCount };
  }

  it('counts 404 as success when emptying Trash', () => {
    const err404 = new Error('Not Found');
    err404.status = 404;

    const results = [
      { status: 'fulfilled', value: 'msg1' },
      { status: 'rejected', reason: err404 },
      { status: 'fulfilled', value: 'msg3' },
      { status: 'rejected', reason: err404 },
    ];

    const { successCount, failCount } = processEmptyFolderResults(results);
    expect(successCount).toBe(4);
    expect(failCount).toBe(0);
  });

  it('still counts real errors when emptying Junk', () => {
    const err404 = new Error('Not Found');
    err404.status = 404;
    const err500 = new Error('Server Error');
    err500.status = 500;

    const results = [
      { status: 'fulfilled', value: 'msg1' },
      { status: 'rejected', reason: err404 },
      { status: 'rejected', reason: err500 },
    ];

    const { successCount, failCount } = processEmptyFolderResults(results);
    expect(successCount).toBe(2);
    expect(failCount).toBe(1);
  });
});

// ---------------------------------------------------------------------------
// Single deleteMessage 404 handling
// ---------------------------------------------------------------------------

describe('deleteMessage 404 handling', () => {
  /**
   * Simulates the catch block logic from deleteMessage
   * Returns true if the error should be queued for retry, false if it's a 404
   */
  function shouldQueueForRetry(err) {
    const is404 = err?.status === 404 || /not (found|exist)/i.test(err?.message || '');
    return !is404;
  }

  it('does not queue retry for 404 error', () => {
    const err = new Error('Not Found');
    err.status = 404;
    expect(shouldQueueForRetry(err)).toBe(false);
  });

  it('does not queue retry for "does not exist" error', () => {
    const err = new Error('Message does not exist');
    expect(shouldQueueForRetry(err)).toBe(false);
  });

  it('queues retry for 500 error', () => {
    const err = new Error('Internal Server Error');
    err.status = 500;
    expect(shouldQueueForRetry(err)).toBe(true);
  });

  it('queues retry for network error', () => {
    const err = new Error('Network error');
    expect(shouldQueueForRetry(err)).toBe(true);
  });

  it('queues retry for timeout error', () => {
    const err = new Error('Request timeout');
    err.status = 408;
    expect(shouldQueueForRetry(err)).toBe(true);
  });
});
