/**
 * Data Access Layer - Repository Pattern
 * Typed functions for all database operations
 */

import { eq, inArray, and, gte, lte, desc, sql } from 'drizzle-orm';
import { db } from './index';
import {
  submissions,
  remediationLog,
  auditLog,
  type Submission,
  type NewSubmission,
  type NewRemediationLogEntry,
  type NewAuditLogEntry,
} from './schema';
import type {
  RegistrationInput,
  SubmissionRecord,
  SubmissionStatus,
  SidChain,
  RemediationEntry,
} from '../types';

// ============================================================
// SUBMISSION OPERATIONS
// ============================================================

/**
 * Create a new submission record
 */
export async function createSubmission(
  input: RegistrationInput
): Promise<SubmissionRecord> {
  const [submission] = await db
    .insert(submissions)
    .values({
      externalId: input.externalId,
      inputData: input as any,
      sidChain: {},
      notifyWebhook: input.notifyWebhook,
      notifyEmail: input.notifyEmail,
    })
    .returning();

  return mapToSubmissionRecord(submission);
}

/**
 * Get a submission by ID
 */
export async function getSubmission(id: string): Promise<SubmissionRecord | null> {
  const [submission] = await db
    .select()
    .from(submissions)
    .where(eq(submissions.id, id))
    .limit(1);

  return submission ? mapToSubmissionRecord(submission) : null;
}

/**
 * Update submission status and optional fields
 */
export async function updateSubmissionStatus(
  id: string,
  status: SubmissionStatus,
  updates?: Partial<{
    failureReason: string;
    brandTrustScore: number;
    brandSubmittedAt: Date;
    brandResolvedAt: Date;
    campaignSubmittedAt: Date;
    campaignResolvedAt: Date;
    completedAt: Date;
  }>
): Promise<void> {
  await db
    .update(submissions)
    .set({
      status,
      updatedAt: new Date(),
      ...updates,
    })
    .where(eq(submissions.id, id));
}

/**
 * Update the SID chain for a submission
 */
export async function updateSidChain(id: string, sidChain: SidChain): Promise<void> {
  await db
    .update(submissions)
    .set({
      sidChain: sidChain as any,
      updatedAt: new Date(),
    })
    .where(eq(submissions.id, id));
}

/**
 * Increment attempt count
 */
export async function incrementAttemptCount(id: string): Promise<void> {
  await db
    .update(submissions)
    .set({
      attemptCount: sql`${submissions.attemptCount} + 1`,
      updatedAt: new Date(),
    })
    .where(eq(submissions.id, id));
}

/**
 * Update landing page URLs
 */
export async function updateLandingPages(
  id: string,
  urls: {
    landingPageUrl: string;
    privacyPolicyUrl: string;
    termsUrl: string;
  }
): Promise<void> {
  await db
    .update(submissions)
    .set({
      ...urls,
      updatedAt: new Date(),
    })
    .where(eq(submissions.id, id));
}

/**
 * Get submissions by status
 */
export async function getSubmissionsByStatus(
  status: SubmissionStatus | SubmissionStatus[]
): Promise<SubmissionRecord[]> {
  const statusArray = Array.isArray(status) ? status : [status];
  const results = await db
    .select()
    .from(submissions)
    .where(inArray(submissions.status, statusArray))
    .orderBy(desc(submissions.createdAt));

  return results.map(mapToSubmissionRecord);
}

/**
 * Get pending submissions (ready to process)
 */
export async function getPendingSubmissions(): Promise<SubmissionRecord[]> {
  return getSubmissionsByStatus([
    'pending',
    'creating_profile',
    'profile_submitted',
    'creating_brand',
    'brand_pending',
    'creating_campaign',
    'campaign_pending',
  ]);
}

/**
 * Get failed submissions
 */
export async function getFailedSubmissions(): Promise<SubmissionRecord[]> {
  return getSubmissionsByStatus(['brand_failed', 'campaign_failed', 'manual_review']);
}

/**
 * Get all submissions with optional filters
 */
export async function getAllSubmissions(filters?: {
  status?: SubmissionStatus[];
  externalId?: string;
  startDate?: Date;
  endDate?: Date;
  limit?: number;
  offset?: number;
}): Promise<SubmissionRecord[]> {
  let query = db.select().from(submissions);

  const conditions = [];
  if (filters?.status) {
    conditions.push(inArray(submissions.status, filters.status));
  }
  if (filters?.externalId) {
    conditions.push(eq(submissions.externalId, filters.externalId));
  }
  if (filters?.startDate) {
    conditions.push(gte(submissions.createdAt, filters.startDate));
  }
  if (filters?.endDate) {
    conditions.push(lte(submissions.createdAt, filters.endDate));
  }

  if (conditions.length > 0) {
    query = query.where(and(...conditions)) as any;
  }

  query = query.orderBy(desc(submissions.createdAt)) as any;

  if (filters?.limit) {
    query = query.limit(filters.limit) as any;
  }
  if (filters?.offset) {
    query = query.offset(filters.offset) as any;
  }

  const results = await query;
  return results.map(mapToSubmissionRecord);
}

// ============================================================
// REMEDIATION LOG OPERATIONS
// ============================================================

/**
 * Add a remediation log entry
 */
export async function addRemediationLog(
  entry: Omit<NewRemediationLogEntry, 'id' | 'createdAt'>
): Promise<void> {
  await db.insert(remediationLog).values(entry);
}

/**
 * Get remediation history for a submission
 */
export async function getRemediationHistory(
  submissionId: string
): Promise<RemediationEntry[]> {
  const entries = await db
    .select()
    .from(remediationLog)
    .where(eq(remediationLog.submissionId, submissionId))
    .orderBy(desc(remediationLog.createdAt));

  return entries.map((entry) => ({
    timestamp: entry.createdAt,
    failureReason: entry.failureReason,
    fixApplied: entry.fixApplied,
    fieldsChanged: entry.fieldsChanged as Record<string, { old: string; new: string }>,
    resubmittedAt: entry.resubmittedAt,
  }));
}

// ============================================================
// AUDIT LOG OPERATIONS
// ============================================================

/**
 * Add an audit log entry for a Twilio API call
 */
export async function addAuditLog(
  entry: Omit<NewAuditLogEntry, 'id' | 'createdAt'>
): Promise<void> {
  await db.insert(auditLog).values(entry);
}

/**
 * Get audit trail for a submission
 */
export async function getAuditLog(submissionId: string) {
  return db
    .select()
    .from(auditLog)
    .where(eq(auditLog.submissionId, submissionId))
    .orderBy(desc(auditLog.createdAt));
}

// ============================================================
// STATS & ANALYTICS
// ============================================================

/**
 * Get dashboard statistics
 */
export async function getSubmissionStats() {
  const [stats] = await db
    .select({
      total: sql<number>`count(*)::int`,
      pending: sql<number>`count(*) filter (where status IN ('pending', 'creating_profile', 'profile_submitted', 'creating_brand', 'brand_pending', 'creating_campaign', 'campaign_pending'))::int`,
      completed: sql<number>`count(*) filter (where status = 'completed')::int`,
      brandApproved: sql<number>`count(*) filter (where status IN ('brand_approved', 'creating_campaign', 'campaign_pending', 'campaign_approved', 'completed'))::int`,
      campaignApproved: sql<number>`count(*) filter (where status IN ('campaign_approved', 'completed'))::int`,
      failed: sql<number>`count(*) filter (where status IN ('brand_failed', 'campaign_failed', 'manual_review'))::int`,
      remediation: sql<number>`count(*) filter (where status = 'remediation')::int`,
    })
    .from(submissions);

  // Calculate success rate
  const successRate =
    stats.total > 0 ? ((stats.completed / stats.total) * 100).toFixed(1) : '0.0';

  // Calculate average time to approval for completed submissions
  const [avgTime] = await db
    .select({
      avgHours: sql<number>`EXTRACT(EPOCH FROM AVG(completed_at - created_at)) / 3600`,
    })
    .from(submissions)
    .where(eq(submissions.status, 'completed'));

  return {
    ...stats,
    successRate: parseFloat(successRate),
    avgTimeToApprovalHours: avgTime?.avgHours
      ? Math.round(avgTime.avgHours * 10) / 10
      : null,
  };
}

// ============================================================
// HELPER FUNCTIONS
// ============================================================

/**
 * Map database record to SubmissionRecord type
 */
async function mapToSubmissionRecord(submission: Submission): Promise<SubmissionRecord> {
  // Get remediation history
  const remediationHistory = await getRemediationHistory(submission.id);

  return {
    id: submission.id,
    input: submission.inputData as RegistrationInput,
    status: submission.status as SubmissionStatus,
    sidChain: (submission.sidChain as SidChain) || {},
    landingPageUrl: submission.landingPageUrl || undefined,
    privacyPolicyUrl: submission.privacyPolicyUrl || undefined,
    termsUrl: submission.termsUrl || undefined,
    brandTrustScore: submission.brandTrustScore || undefined,
    failureReason: submission.failureReason || undefined,
    remediationHistory,
    attemptCount: submission.attemptCount,
    maxAttempts: submission.maxAttempts,
    createdAt: submission.createdAt,
    updatedAt: submission.updatedAt,
    brandSubmittedAt: submission.brandSubmittedAt || undefined,
    brandResolvedAt: submission.brandResolvedAt || undefined,
    campaignSubmittedAt: submission.campaignSubmittedAt || undefined,
    campaignResolvedAt: submission.campaignResolvedAt || undefined,
    completedAt: submission.completedAt || undefined,
  };
}
