import 'server-only';

import { isBefore } from 'date-fns';

import { renderDoctorSummaryReceivedEmail } from '@kit/email-templates';
import { getFullName } from '@kit/shared/utils';
import { getSupabaseServerClient } from '@kit/supabase/server-client';
import { createUserAnalysesApi } from '@kit/user-analyses/api';

import { sendEmailFromTemplate } from '../../../../../../../lib/services/mailer.service';
import { AnalysisResultDetails } from '../schema/doctor-analysis-detail-view.schema';
import {
  AnalysisResponseBase,
  DoctorAnalysisFeedbackTable,
  PaginatedData,
  PaginationParams,
  ResponseTable,
} from '../schema/doctor-analysis.schema';
import { ErrorReason } from '../schema/error.type';

async function enrichAnalysisData(analysisResponses?: AnalysisResponseBase[]) {
  const supabase = getSupabaseServerClient();

  if (!analysisResponses?.length) return [];

  const analysisResponseIds = analysisResponses.map((r) => r.id);
  const medusaOrderIds = analysisResponses.map(
    (r) => r.analysis_order_id.medusa_order_id,
  );
  const userIds = analysisResponses.map((response) => response.user_id);

  const [
    { data: doctorFeedbackItems },
    { data: medusaOrderItems },
    { data: analysisResponseElements },
    { data: accounts },
  ] = await Promise.all([
    supabase
      .schema('medreport')
      .from('doctor_analysis_feedback')
      .select('analysis_order_id, user_id, doctor_user_id, status')
      .in(
        'analysis_order_id',
        analysisResponses.map((r) => r.analysis_order_id.id),
      ),
    supabase
      .schema('public')
      .from('order_item')
      .select('order_id, item_id(product_title, product_type)')
      .in('order_id', medusaOrderIds),
    supabase
      .schema('medreport')
      .from('analysis_response_elements')
      .select('*')
      .in('analysis_response_id', analysisResponseIds),
    supabase
      .schema('medreport')
      .from('accounts')
      .select('name, last_name, id, primary_owner_user_id, preferred_locale')
      .in('primary_owner_user_id', userIds),
  ]);

  const doctorUserIds =
    doctorFeedbackItems
      ?.map((item) => item.doctor_user_id)
      .filter((value) => value !== null) || [];

  const { data: doctorAccounts } = doctorUserIds.length
    ? await supabase
        .schema('medreport')
        .from('accounts')
        .select('name, last_name, id, primary_owner_user_id, preferred_locale')
        .in('primary_owner_user_id', doctorUserIds)
    : { data: [] };

  const allAccounts = [...(accounts ?? []), ...(doctorAccounts ?? [])];

  return analysisResponses.map((analysisResponse) => {
    const responseElements =
      analysisResponseElements?.filter(
        (element) => element.analysis_response_id === analysisResponse.id,
      ) || [];

    const firstSampleGivenAt = responseElements.length
      ? responseElements.reduce((earliest, current) =>
          new Date(current.response_time) < new Date(earliest.response_time)
            ? current
            : earliest,
        )?.response_time
      : null;

    const medusaOrder = medusaOrderItems?.find(
      ({ order_id }) =>
        order_id === analysisResponse.analysis_order_id.medusa_order_id,
    );

    const patientAccount = allAccounts?.find(
      ({ primary_owner_user_id }) =>
        analysisResponse.user_id === primary_owner_user_id,
    );

    const feedback = doctorFeedbackItems?.find(
      ({ analysis_order_id }) =>
        analysis_order_id === analysisResponse.analysis_order_id.id,
    );

    const doctorAccount = allAccounts?.find(
      ({ primary_owner_user_id }) =>
        feedback?.doctor_user_id === primary_owner_user_id,
    );

    const order = {
      title: medusaOrder?.item_id.product_title,
      isPackage:
        medusaOrder?.item_id.product_type?.toLowerCase() === 'analysis package',
      analysisOrderId: analysisResponse.analysis_order_id.id,
      status: analysisResponse.order_status,
    };

    if (!patientAccount || !analysisResponse) {
      throw new Error('Invalid data');
    }

    return {
      ...analysisResponse,
      elements: responseElements,
      firstSampleGivenAt,
      patient: patientAccount,
      doctor: doctorAccount,
      order,
      feedback,
    };
  });
}

export async function getUserInProgressResponses({
  page = 1,
  pageSize = 10,
}: PaginationParams): Promise<PaginatedData<ResponseTable>> {
  const supabase = getSupabaseServerClient();
  const {
    data: { user },
  } = await supabase.auth.getUser();

  if (!user) {
    throw new Error('No user logged in.');
  }

  const { data: inProgressFeedback } = await supabase
    .schema('medreport')
    .from('doctor_analysis_feedback')
    .select('analysis_order_id')
    .eq('doctor_user_id', user.id)
    .neq('status', 'COMPLETED');

  if (!inProgressFeedback?.length) {
    return {
      data: [],
      pagination: { currentPage: page, totalPages: 0, totalCount: 0, pageSize },
    };
  }

  const analysisOrderIds = inProgressFeedback.map((f) => f.analysis_order_id);
  const offset = (page - 1) * pageSize;

  const {
    data: analysisResponses,
    error,
    count,
  } = await supabase
    .schema('medreport')
    .from('analysis_responses')
    .select(
      `
      *,
      analysis_order_id(id, medusa_order_id, analysis_element_ids)
    `,
      { count: 'exact' },
    )
    .in('analysis_order_id', analysisOrderIds)
    .range(offset, offset + pageSize - 1)
    .order('created_at', { ascending: false });

  if (error) {
    throw new Error('Something went wrong');
  }

  const enrichedData = await enrichAnalysisData(analysisResponses);
  const totalCount = count || 0;
  const totalPages = Math.ceil(totalCount / pageSize);

  return {
    data: enrichedData,
    pagination: { currentPage: page, totalPages, totalCount, pageSize },
  };
}

export async function getUserDoneResponses({
  page = 1,
  pageSize = 10,
}: PaginationParams): Promise<PaginatedData<ResponseTable>> {
  const supabase = getSupabaseServerClient();
  const {
    data: { user },
  } = await supabase.auth.getUser();

  if (!user) {
    throw new Error('No user logged in.');
  }

  const { data: completedFeedback } = await supabase
    .schema('medreport')
    .from('doctor_analysis_feedback')
    .select('analysis_order_id')
    .eq('doctor_user_id', user.id)
    .eq('status', 'COMPLETED');

  if (!completedFeedback?.length) {
    return {
      data: [],
      pagination: { currentPage: page, totalPages: 0, totalCount: 0, pageSize },
    };
  }

  const analysisOrderIds = completedFeedback.map((f) => f.analysis_order_id);
  const offset = (page - 1) * pageSize;

  const {
    data: analysisResponses,
    error,
    count,
  } = await supabase
    .schema('medreport')
    .from('analysis_responses')
    .select(
      `
      *,
      analysis_order_id(id, medusa_order_id, status, analysis_element_ids)
    `,
      { count: 'exact' },
    )
    .in('analysis_order_id', analysisOrderIds)
    .range(offset, offset + pageSize - 1)
    .order('created_at', { ascending: false });

  if (error) throw error;

  const enrichedData = await enrichAnalysisData(analysisResponses);
  const totalCount = count || 0;
  const totalPages = Math.ceil(totalCount / pageSize);

  return {
    data: enrichedData,
    pagination: { currentPage: page, totalPages, totalCount, pageSize },
  };
}

export async function getOpenResponses({
  page = 1,
  pageSize = 10,
}: PaginationParams): Promise<PaginatedData<ResponseTable>> {
  const supabase = getSupabaseServerClient();

  const { data: assignedOrderIds } = await supabase
    .schema('medreport')
    .from('doctor_analysis_feedback')
    .select('analysis_order_id')
    .not('doctor_user_id', 'is', null);

  const assignedIds = assignedOrderIds?.map((f) => f.analysis_order_id) || [];
  const offset = (page - 1) * pageSize;

  let query = supabase
    .schema('medreport')
    .from('analysis_responses')
    .select(
      `
      *,
      analysis_order_id(id, medusa_order_id, analysis_element_ids)
    `,
      { count: 'exact' },
    )
    .order('created_at', { ascending: false });

  if (assignedIds.length > 0) {
    query = query.not('analysis_order_id', 'in', `(${assignedIds.join(',')})`);
  }

  const {
    data: analysisResponses,
    error,
    count,
  } = await query.range(offset, offset + pageSize - 1);
  if (error) throw error;

  const enrichedData = await enrichAnalysisData(analysisResponses);
  const totalCount = count || 0;
  const totalPages = Math.ceil(totalCount / pageSize);

  return {
    data: enrichedData,
    pagination: { currentPage: page, totalPages, totalCount, pageSize },
  };
}

export async function getOtherResponses({
  page = 1,
  pageSize = 10,
}: PaginationParams): Promise<PaginatedData<ResponseTable>> {
  const supabase = getSupabaseServerClient();
  const {
    data: { user },
  } = await supabase.auth.getUser();

  if (!user) {
    throw new Error('No user logged in.');
  }

  const { data: otherFeedback } = await supabase
    .schema('medreport')
    .from('doctor_analysis_feedback')
    .select('analysis_order_id')
    .neq('doctor_user_id', user.id);

  if (!otherFeedback?.length) {
    return {
      data: [],
      pagination: { currentPage: page, totalPages: 0, totalCount: 0, pageSize },
    };
  }

  const analysisOrderIds = otherFeedback.map((f) => f.analysis_order_id);
  const offset = (page - 1) * pageSize;

  const {
    data: analysisResponses,
    error,
    count,
  } = await supabase
    .schema('medreport')
    .from('analysis_responses')
    .select(
      `
      *,
      analysis_order_id(id, medusa_order_id, analysis_element_ids)
    `,
      { count: 'exact' },
    )
    .in('analysis_order_id', analysisOrderIds)
    .range(offset, offset + pageSize - 1)
    .order('created_at', { ascending: false });

  if (error) throw error;

  const enrichedData = await enrichAnalysisData(analysisResponses);
  const totalCount = count || 0;
  const totalPages = Math.ceil(totalCount / pageSize);

  return {
    data: enrichedData,
    pagination: { currentPage: page, totalPages, totalCount, pageSize },
  };
}

export async function getAnalysisResultsForDoctor(
  analysisResponseId: number,
): Promise<AnalysisResultDetails> {
  const supabase = getSupabaseServerClient();

  const { data: analysisResponseElements, error } = await supabase
    .schema('medreport')
    .from(`analysis_response_elements`)
    .select(
      `*,
      analysis_responses(user_id, analysis_order_id(id,medusa_order_id, analysis_element_ids))`,
    )
    .eq('analysis_response_id', analysisResponseId);

  if (error) {
    throw new Error('Something went wrong.');
  }

  const firstAnalysisResponse = analysisResponseElements?.[0];
  const userId = firstAnalysisResponse?.analysis_responses.user_id;
  const medusaOrderId =
    firstAnalysisResponse?.analysis_responses?.analysis_order_id
      ?.medusa_order_id;

  if (!analysisResponseElements?.length || !userId || !medusaOrderId) {
    throw new Error('Failed to retrieve full analysis data.');
  }

  const responseElementAnalysisElementOriginalIds =
    analysisResponseElements.map(
      ({ analysis_element_original_id }) => analysis_element_original_id,
    );

  const [
    { data: medusaOrderItems, error: medusaOrderError },
    { data: accountWithParams, error: accountError },
    { data: doctorFeedback, error: feedbackError },
    { data: previousAnalyses, error: previousAnalysesError },
  ] = await Promise.all([
    supabase
      .schema('public')
      .from('order_item')
      .select(`order_id, item_id(product_title, product_type)`)
      .eq('order_id', medusaOrderId),
    supabase
      .schema('medreport')
      .from('accounts')
      .select(
        `primary_owner_user_id, id, name, last_name, personal_code, phone, email, preferred_locale,
      accountParams:account_params(height,weight)`,
      )
      .eq('is_personal_account', true)
      .eq('primary_owner_user_id', userId)
      .limit(1),
    supabase
      .schema('medreport')
      .from('doctor_analysis_feedback')
      .select(`*`)
      .eq(
        'analysis_order_id',
        firstAnalysisResponse.analysis_responses.analysis_order_id.id,
      )
      .limit(1),
    supabase
      .schema('medreport')
      .from('analysis_response_elements')
      .select(
        `
    *,
    analysis_responses!inner(
      user_id
    )
  `,
      )
      .in(
        'analysis_element_original_id',
        responseElementAnalysisElementOriginalIds,
      )
      .not(
        'analysis_response_id',
        'eq',
        firstAnalysisResponse.analysis_response_id,
      )
      .eq(
        'analysis_responses.user_id',
        firstAnalysisResponse.analysis_responses.user_id,
      )
      .order('response_time'),
  ]);

  if (
    medusaOrderError ||
    accountError ||
    feedbackError ||
    previousAnalysesError
  ) {
    throw new Error('Something went wrong.');
  }

  if (!accountWithParams?.[0]) {
    throw new Error('Account not found.');
  }

  const {
    primary_owner_user_id,
    id: accountId,
    name,
    email,
    last_name,
    personal_code,
    phone,
    accountParams,
    preferred_locale,
  } = accountWithParams[0];

  const analysisResponseElementsWithPreviousData = [];
  for (const analysisResponseElement of analysisResponseElements) {
    const latestPreviousAnalysis = previousAnalyses.find(
      ({ analysis_element_original_id, response_time }) =>
        analysis_element_original_id ===
          analysisResponseElement.analysis_element_original_id &&
        isBefore(
          new Date(response_time),
          new Date(analysisResponseElement.response_time),
        ),
    );
    analysisResponseElementsWithPreviousData.push({
      ...analysisResponseElement,
      latestPreviousAnalysis,
    });
  }

  return {
    analysisResponse: analysisResponseElementsWithPreviousData,
    order: {
      title: medusaOrderItems?.[0]?.item_id.product_title ?? '-',
      isPackage:
        medusaOrderItems?.[0]?.item_id.product_type?.toLowerCase() ===
        'analysis package',
      analysisOrderId:
        firstAnalysisResponse.analysis_responses.analysis_order_id.id,
    },
    doctorFeedback: doctorFeedback?.[0],
    patient: {
      preferred_locale,
      userId: primary_owner_user_id,
      accountId,
      firstName: name,
      lastName: last_name,
      personalCode: personal_code,
      phone,
      email,
      height: accountParams?.height,
      weight: accountParams?.weight,
    },
  };
}

export async function selectJob(analysisOrderId: number, userId: string) {
  const supabase = getSupabaseServerClient();
  const {
    data: { user },
  } = await supabase.auth.getUser();

  if (!user?.id) {
    throw new Error('No user logged in.');
  }

  const { data: existingFeedback, error: existingFeedbackError } =
    await supabase
      .schema('medreport')
      .from('doctor_analysis_feedback')
      .select('doctor_user_id')
      .eq('analysis_order_id', analysisOrderId)
      .limit(1);

  const jobAssignedToUserId = existingFeedback?.[0]?.doctor_user_id;

  if (!!jobAssignedToUserId && jobAssignedToUserId !== user.id) {
    throw new Error(ErrorReason.JOB_ASSIGNED);
  }

  const { data, error } = await supabase
    .schema('medreport')
    .from('doctor_analysis_feedback')
    .upsert(
      {
        doctor_user_id: user.id,
        user_id: userId,
        analysis_order_id: analysisOrderId,
      },
      { onConflict: 'analysis_order_id' },
    );

  if (error || existingFeedbackError) {
    throw new Error('Something went wrong');
  }

  return data;
}

export async function unselectJob(analysisOrderId: number) {
  const supabase = getSupabaseServerClient();
  const {
    data: { user },
  } = await supabase.auth.getUser();

  if (!user?.id) {
    throw new Error('No user logged in.');
  }

  const { data: currentDoctorFeedback, error: currentDoctorFeedbackError } =
    await supabase
      .schema('medreport')
      .from('doctor_analysis_feedback')
      .select('id,doctor_user_id')
      .eq('analysis_order_id', analysisOrderId)
      .eq('doctor_user_id', user.id)
      .limit(1);

  if (!currentDoctorFeedback) {
    throw new Error(
      'Current user not assigned to give feedback to this set of analyses.',
    );
  }

  if (currentDoctorFeedbackError) {
    throw new Error('Error retrieving job data');
  }

  const { data, error } = await supabase
    .schema('medreport')
    .from('doctor_analysis_feedback')
    .update({
      doctor_user_id: null,
    })
    .eq('analysis_order_id', analysisOrderId)
    .eq('doctor_user_id', user.id);

  if (error) {
    throw new Error('Something went wrong');
  }

  return data;
}

export async function submitFeedback(
  analysisOrderId: DoctorAnalysisFeedbackTable['analysis_order_id'],
  userId: DoctorAnalysisFeedbackTable['user_id'],
  value: DoctorAnalysisFeedbackTable['value'],
  status: DoctorAnalysisFeedbackTable['status'],
) {
  const supabase = getSupabaseServerClient();
  const {
    data: { user },
  } = await supabase.auth.getUser();

  if (!user?.id) {
    throw new Error('No user logged in.');
  }

  const { data, error } = await supabase
    .schema('medreport')
    .from('doctor_analysis_feedback')
    .upsert(
      {
        doctor_user_id: user?.id,
        user_id: userId,
        analysis_order_id: analysisOrderId,
        value,
        status,
      },
      { onConflict: 'analysis_order_id' },
    );

  if (error) {
    throw new Error('Something went wrong');
  }

  if (status === 'COMPLETED') {
    const { data: analysisOrder } = await supabase
      .schema('medreport')
      .from('analysis_orders')
      .select('medusa_order_id, id')
      .eq('id', analysisOrderId)
      .limit(1)
      .throwOnError();
    const [{ data: recipient }] = await Promise.all([
      supabase
        .schema('medreport')
        .from('accounts')
        .select('name, last_name, email, preferred_locale')
        .eq('is_personal_account', true)
        .eq('primary_owner_user_id', userId)
        .throwOnError(),
      createUserAnalysesApi(supabase).updateAnalysisOrderStatus({
        orderId: analysisOrderId,
        orderStatus: 'COMPLETED',
      }),
    ]);

    if (!recipient?.[0]?.email) {
      throw new Error('Could not find user email.');
    }

    if (!analysisOrder?.[0]?.id) {
      throw new Error('Could not retrieve order.');
    }

    const { preferred_locale, name, last_name, email } = recipient[0];

    await sendEmailFromTemplate(
      renderDoctorSummaryReceivedEmail,
      {
        language: preferred_locale ?? 'et',
        recipientName: getFullName(name, last_name),
        orderNr: analysisOrder?.[0]?.medusa_order_id ?? '',
        analysisOrderId: analysisOrder[0].id,
      },
      email,
    );
  }

  return data;
}
