import { v4 as uuidv4 } from 'uuid';
import { db } from '../application/database';
import { ResponseError } from '../error/response-error';
import {
  ContactResponse,
  toContactResponse,
  CreateContactRequest,
  BulkUpdateContactRequest,
  Contact,
} from '../model/contact-model';
import { ContactValidation } from '../validation/contact-validation';
import { Validation } from '../validation/validation';

export class ContactService {
  static async create(request: CreateContactRequest): Promise<ContactResponse> {
    // Validate the create request
    const createRequest = Validation.validate(ContactValidation.CREATE, request);

    // Generate a UUID for the new contact
    const contactId = uuidv4();

    // Insert the new contact into the database with the UUID
    await db.query('INSERT INTO contact (id, name, value) VALUES (?, ?, ?)', [
      contactId,
      createRequest.name,
      createRequest.value,
    ]);

    // Retrieve the newly created contact by the UUID
    const contact = await db.queryOne<Contact>('SELECT * FROM contact WHERE id = ? LIMIT 1', [contactId]);

    if (!contact) {
      throw new ResponseError(500, 'Failed to create contact');
    }

    return toContactResponse(contact);
  }

  static async bulkUpdate(requests: BulkUpdateContactRequest[]): Promise<void> {
    // Validate the bulk update request
    const bulkUpdateRequest = Validation.validate(ContactValidation.BULK_UPDATE, requests);

    // Start a transaction for batch processing
    await db.transaction(async (conn) => {
      for (const updateRequest of bulkUpdateRequest) {
        const fieldsToUpdate = [];
        const values = [];

        // Add `name` to the update if provided
        if (updateRequest.name !== undefined) {
          fieldsToUpdate.push('name = ?');
          values.push(updateRequest.name);
        }

        // Add `value` to the update if provided
        if (updateRequest.value !== undefined) {
          fieldsToUpdate.push('value = ?');
          values.push(updateRequest.value);
        }

        // Skip this update if no fields are provided
        if (fieldsToUpdate.length === 0) continue;

        // Add ID at the end for the WHERE clause
        values.push(updateRequest.id);

        // Execute the dynamic update query
        const query = `UPDATE contact SET ${fieldsToUpdate.join(', ')} WHERE id = ?`;
        await conn.query(query, values);
      }
    });
  }

  static async delete(id: string): Promise<ContactResponse> {
    // Retrieve the contact before deleting it
    const contact = await db.queryOne<Contact>('SELECT * FROM contact WHERE id = ?', [id]);

    if (!contact) {
      throw new ResponseError(404, 'Contact not found');
    }

    // Delete the contact from the database
    await db.query('DELETE FROM contact WHERE id = ?', [id]);

    return toContactResponse(contact);
  }

  static async getAll(): Promise<ContactResponse[]> {
    // Retrieve all contacts from the database
    const contacts = await db.query<Contact>('SELECT * FROM contact ORDER BY created_at ASC');

    // Map each contact to the response format
    return contacts.map(toContactResponse);
  }

  static async getByName(name: string): Promise<ContactResponse> {
    // Retrieve a single contact by name
    const contact = await db.queryOne<Contact>('SELECT * FROM contact WHERE name = ?', [name]);

    if (!contact) {
      throw new ResponseError(404, 'Contact not found');
    }

    return toContactResponse(contact);
  }
}
