import { db } from '../db/client.js';
import type {
  AuthContext,
  AuthSessionRow,
  BillingInterval,
  BillingPlan,
  IntegrationTokenRow,
  IntegrationTokenScope,
  MagicLinkTokenRow,
  OAuthStateRow,
  ProviderKey,
  RescueVerificationStatus,
  SubscriptionStatus,
  UserRow,
  WorkspaceMemberRow,
  WorkspaceRole,
  WorkspaceRow,
  WorkspaceType,
} from '../types.js';

const mapSessionAuthRow = (
  row: AuthSessionRow &
    UserRow &
    WorkspaceRow &
    WorkspaceMemberRow & {
      session_id: string;
      session_user_id: string;
      session_active_workspace_id: number;
      session_token_hash: string;
      session_expires_at: string;
      session_created_at: string;
      user_id_row: string;
      user_email: string;
      user_password_hash: string | null;
      user_name: string;
      user_created_at: string;
      workspace_id_row: number;
      workspace_name: string;
      workspace_workspace_type: WorkspaceType;
      workspace_billing_email: string | null;
      workspace_billing_plan: BillingPlan;
      workspace_billing_interval: BillingInterval;
      workspace_subscription_status: SubscriptionStatus;
      workspace_stripe_customer_id: string | null;
      workspace_stripe_subscription_id: string | null;
      workspace_rescue_verification_status: RescueVerificationStatus;
      workspace_created_at: string;
      workspace_updated_at: string;
      membership_id_row: string;
      membership_workspace_id: number;
      membership_user_id: string | null;
      membership_invite_email: string;
      membership_name: string;
      membership_role: WorkspaceRole;
      membership_accepted_at: string | null;
      membership_created_at: string;
    },
  token: string,
): AuthContext => ({
  user: {
    id: row.user_id_row,
    email: row.user_email,
    password_hash: row.user_password_hash,
    name: row.user_name,
    created_at: row.user_created_at,
  },
  session: {
    id: row.session_id,
    user_id: row.session_user_id,
    active_workspace_id: row.session_active_workspace_id,
    token_hash: row.session_token_hash,
    expires_at: row.session_expires_at,
    created_at: row.session_created_at,
  },
  workspace: {
    id: row.workspace_id_row,
    name: row.workspace_name,
    workspace_type: row.workspace_workspace_type,
    billing_email: row.workspace_billing_email,
    billing_plan: row.workspace_billing_plan,
    billing_interval: row.workspace_billing_interval,
    subscription_status: row.workspace_subscription_status,
    stripe_customer_id: row.workspace_stripe_customer_id,
    stripe_subscription_id: row.workspace_stripe_subscription_id,
    rescue_verification_status: row.workspace_rescue_verification_status,
    created_at: row.workspace_created_at,
    updated_at: row.workspace_updated_at,
  },
  membership: {
    id: row.membership_id_row,
    workspace_id: row.membership_workspace_id,
    user_id: row.membership_user_id,
    invite_email: row.membership_invite_email,
    name: row.membership_name,
    role: row.membership_role,
    accepted_at: row.membership_accepted_at,
    created_at: row.membership_created_at,
  },
  token,
  authType: 'session',
});

const mapIntegrationTokenAuthRow = (
  row: IntegrationTokenRow &
    UserRow &
    WorkspaceRow &
    WorkspaceMemberRow & {
      integration_token_id: string;
      integration_token_user_id: string;
      integration_token_workspace_id: number;
      integration_token_name: string;
      integration_token_token_hash: string;
      integration_token_token_prefix: string;
      integration_token_scope: IntegrationTokenScope;
      integration_token_last_used_at: string | null;
      integration_token_expires_at: string | null;
      integration_token_revoked_at: string | null;
      integration_token_created_at: string;
      user_id_row: string;
      user_email: string;
      user_password_hash: string | null;
      user_name: string;
      user_created_at: string;
      workspace_id_row: number;
      workspace_name: string;
      workspace_workspace_type: WorkspaceType;
      workspace_billing_email: string | null;
      workspace_billing_plan: BillingPlan;
      workspace_billing_interval: BillingInterval;
      workspace_subscription_status: SubscriptionStatus;
      workspace_stripe_customer_id: string | null;
      workspace_stripe_subscription_id: string | null;
      workspace_rescue_verification_status: RescueVerificationStatus;
      workspace_created_at: string;
      workspace_updated_at: string;
      membership_id_row: string;
      membership_workspace_id: number;
      membership_user_id: string | null;
      membership_invite_email: string;
      membership_name: string;
      membership_role: WorkspaceRole;
      membership_accepted_at: string | null;
      membership_created_at: string;
    },
  token: string,
): AuthContext => ({
  user: {
    id: row.user_id_row,
    email: row.user_email,
    password_hash: row.user_password_hash,
    name: row.user_name,
    created_at: row.user_created_at,
  },
  session: {
    id: row.integration_token_id,
    user_id: row.integration_token_user_id,
    active_workspace_id: row.integration_token_workspace_id,
    token_hash: row.integration_token_token_hash,
    expires_at: row.integration_token_expires_at ?? '',
    created_at: row.integration_token_created_at,
  },
  workspace: {
    id: row.workspace_id_row,
    name: row.workspace_name,
    workspace_type: row.workspace_workspace_type,
    billing_email: row.workspace_billing_email,
    billing_plan: row.workspace_billing_plan,
    billing_interval: row.workspace_billing_interval,
    subscription_status: row.workspace_subscription_status,
    stripe_customer_id: row.workspace_stripe_customer_id,
    stripe_subscription_id: row.workspace_stripe_subscription_id,
    rescue_verification_status: row.workspace_rescue_verification_status,
    created_at: row.workspace_created_at,
    updated_at: row.workspace_updated_at,
  },
  membership: {
    id: row.membership_id_row,
    workspace_id: row.membership_workspace_id,
    user_id: row.membership_user_id,
    invite_email: row.membership_invite_email,
    name: row.membership_name,
    role: row.membership_role,
    accepted_at: row.membership_accepted_at,
    created_at: row.membership_created_at,
  },
  token,
  authType: 'integration_token',
  integrationToken: {
    id: row.integration_token_id,
    user_id: row.integration_token_user_id,
    workspace_id: row.integration_token_workspace_id,
    name: row.integration_token_name,
    token_hash: row.integration_token_token_hash,
    token_prefix: row.integration_token_token_prefix,
    scope: row.integration_token_scope,
    last_used_at: row.integration_token_last_used_at,
    expires_at: row.integration_token_expires_at,
    revoked_at: row.integration_token_revoked_at,
    created_at: row.integration_token_created_at,
  },
});

export const createAuthSession = async (userId: string, activeWorkspaceId: number, tokenHash: string, expiresAt: string) => {
  const result = await db.query<AuthSessionRow>(
    `INSERT INTO auth_sessions (user_id, active_workspace_id, token_hash, expires_at)
     VALUES ($1, $2, $3, $4)
     RETURNING id, user_id, active_workspace_id, token_hash, expires_at::text, created_at`,
    [userId, activeWorkspaceId, tokenHash, expiresAt],
  );

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

export const deleteAuthSession = async (sessionId: string) => {
  await db.query('DELETE FROM auth_sessions WHERE id = $1', [sessionId]);
};

export const updateSessionWorkspace = async (sessionId: string, workspaceId: number) => {
  await db.query(
    `UPDATE auth_sessions
     SET active_workspace_id = $2
     WHERE id = $1`,
    [sessionId, workspaceId],
  );
};

export const deleteExpiredMagicLinkTokens = async () => {
  await db.query(
    `DELETE FROM magic_link_tokens
     WHERE expires_at <= CURRENT_TIMESTAMP`,
  );
};

export const createMagicLinkToken = async (email: string, name: string | null, tokenHash: string, redirectTo: string, expiresAt: string) => {
  await db.query(
    `INSERT INTO magic_link_tokens (email, name, token_hash, redirect_to, expires_at)
     VALUES ($1, $2, $3, $4, $5)`,
    [email, name, tokenHash, redirectTo, expiresAt],
  );
};

export const consumeMagicLinkToken = async (tokenHash: string) => {
  const result = await db.query<MagicLinkTokenRow>(
    `DELETE FROM magic_link_tokens
     WHERE token_hash = $1
       AND expires_at > CURRENT_TIMESTAMP
     RETURNING id, email, name, token_hash, redirect_to, expires_at::text, created_at`,
    [tokenHash],
  );

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

export const findUserByEmail = async (email: string) => {
  const result = await db.query<UserRow>(
    `SELECT id, email, password_hash, name, created_at
     FROM users
     WHERE email = $1`,
    [email],
  );

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

export const createUser = async (email: string, name: string) => {
  const result = await db.query<UserRow>(
    `INSERT INTO users (email, name)
     VALUES ($1, $2)
     RETURNING id, email, password_hash, name, created_at`,
    [email, name],
  );

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

export const updateUserName = async (userId: string, name: string) => {
  const result = await db.query<UserRow>(
    `UPDATE users
     SET name = $2
     WHERE id = $1
     RETURNING id, email, password_hash, name, created_at`,
    [userId, name],
  );

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

export const createOAuthState = async (id: string, providerKey: ProviderKey, codeVerifier: string, redirectTo: string, expiresAt: string) => {
  await db.query(
    `INSERT INTO oauth_states (id, provider_key, code_verifier, redirect_to, expires_at)
     VALUES ($1, $2, $3, $4, $5)`,
    [id, providerKey, codeVerifier, redirectTo, expiresAt],
  );
};

export const consumeOAuthState = async (stateId: string, providerKey: ProviderKey) => {
  const result = await db.query<OAuthStateRow>(
    `DELETE FROM oauth_states
     WHERE id = $1
       AND provider_key = $2
       AND expires_at > CURRENT_TIMESTAMP
     RETURNING id, provider_key, code_verifier, redirect_to, expires_at::text`,
    [stateId, providerKey],
  );

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

export const findUserByProviderAccount = async (providerKey: ProviderKey, providerSubject: string) => {
  const result = await db.query<UserRow>(
    `SELECT users.id, users.email, users.password_hash, users.name, users.created_at
     FROM auth_accounts
     INNER JOIN users ON users.id = auth_accounts.user_id
     WHERE auth_accounts.provider_key = $1
       AND auth_accounts.provider_subject = $2`,
    [providerKey, providerSubject],
  );

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

export const linkAuthAccount = async (userId: string, providerKey: ProviderKey, providerSubject: string, providerEmail: string) => {
  await db.query(
    `INSERT INTO auth_accounts (user_id, provider_key, provider_subject, provider_email)
     VALUES ($1, $2, $3, $4)
     ON CONFLICT (provider_key, provider_subject) DO NOTHING`,
    [userId, providerKey, providerSubject, providerEmail],
  );
};

export const resolveAuth = async (tokenHash: string, token: string) => {
  const result = await db.query<
    AuthSessionRow &
      UserRow &
      WorkspaceRow &
      WorkspaceMemberRow & {
        session_id: string;
        session_user_id: string;
        session_active_workspace_id: number;
        session_token_hash: string;
        session_expires_at: string;
        session_created_at: string;
        user_id_row: string;
        user_email: string;
        user_password_hash: string | null;
        user_name: string;
        user_created_at: string;
        workspace_id_row: number;
        workspace_name: string;
        workspace_workspace_type: WorkspaceType;
        workspace_billing_email: string | null;
        workspace_billing_plan: BillingPlan;
        workspace_billing_interval: BillingInterval;
        workspace_subscription_status: SubscriptionStatus;
        workspace_stripe_customer_id: string | null;
        workspace_stripe_subscription_id: string | null;
        workspace_rescue_verification_status: RescueVerificationStatus;
        workspace_created_at: string;
        workspace_updated_at: string;
        membership_id_row: string;
        membership_workspace_id: number;
        membership_user_id: string | null;
        membership_invite_email: string;
        membership_name: string;
        membership_role: WorkspaceRole;
        membership_accepted_at: string | null;
        membership_created_at: string;
      }
  >(
    `SELECT
      auth_sessions.id AS session_id,
      auth_sessions.user_id AS session_user_id,
      auth_sessions.active_workspace_id AS session_active_workspace_id,
      auth_sessions.token_hash AS session_token_hash,
      auth_sessions.expires_at::text AS session_expires_at,
      auth_sessions.created_at AS session_created_at,
      users.id AS user_id_row,
      users.email AS user_email,
      users.password_hash AS user_password_hash,
      users.name AS user_name,
      users.created_at AS user_created_at,
      workspaces.id AS workspace_id_row,
      workspaces.name AS workspace_name,
      workspaces.workspace_type AS workspace_workspace_type,
      workspaces.billing_email AS workspace_billing_email,
      workspaces.billing_plan AS workspace_billing_plan,
      workspaces.billing_interval AS workspace_billing_interval,
      workspaces.subscription_status AS workspace_subscription_status,
      workspaces.stripe_customer_id AS workspace_stripe_customer_id,
      workspaces.stripe_subscription_id AS workspace_stripe_subscription_id,
      workspaces.rescue_verification_status AS workspace_rescue_verification_status,
      workspaces.created_at AS workspace_created_at,
      workspaces.updated_at AS workspace_updated_at,
      workspace_members.id AS membership_id_row,
      workspace_members.workspace_id AS membership_workspace_id,
      workspace_members.user_id AS membership_user_id,
      COALESCE(workspace_members.invite_email, workspace_members.email) AS membership_invite_email,
      workspace_members.name AS membership_name,
      workspace_members.role AS membership_role,
      workspace_members.accepted_at::text AS membership_accepted_at,
      workspace_members.created_at AS membership_created_at
    FROM auth_sessions
    INNER JOIN users ON users.id = auth_sessions.user_id
    INNER JOIN workspaces ON workspaces.id = auth_sessions.active_workspace_id
    INNER JOIN workspace_members
      ON workspace_members.workspace_id = workspaces.id
     AND workspace_members.user_id = users.id
    WHERE auth_sessions.token_hash = $1
      AND auth_sessions.expires_at > CURRENT_TIMESTAMP`,
    [tokenHash],
  );

  return result.rows[0] ? mapSessionAuthRow(result.rows[0], token) : null;
};

export const resolveIntegrationTokenAuth = async (tokenHash: string, token: string) => {
  const result = await db.query<
    IntegrationTokenRow &
      UserRow &
      WorkspaceRow &
      WorkspaceMemberRow & {
        integration_token_id: string;
        integration_token_user_id: string;
        integration_token_workspace_id: number;
        integration_token_name: string;
        integration_token_token_hash: string;
        integration_token_token_prefix: string;
        integration_token_scope: IntegrationTokenScope;
        integration_token_last_used_at: string | null;
        integration_token_expires_at: string | null;
        integration_token_revoked_at: string | null;
        integration_token_created_at: string;
        user_id_row: string;
        user_email: string;
        user_password_hash: string | null;
        user_name: string;
        user_created_at: string;
        workspace_id_row: number;
        workspace_name: string;
        workspace_workspace_type: WorkspaceType;
        workspace_billing_email: string | null;
        workspace_billing_plan: BillingPlan;
        workspace_billing_interval: BillingInterval;
        workspace_subscription_status: SubscriptionStatus;
        workspace_stripe_customer_id: string | null;
        workspace_stripe_subscription_id: string | null;
        workspace_rescue_verification_status: RescueVerificationStatus;
        workspace_created_at: string;
        workspace_updated_at: string;
        membership_id_row: string;
        membership_workspace_id: number;
        membership_user_id: string | null;
        membership_invite_email: string;
        membership_name: string;
        membership_role: WorkspaceRole;
        membership_accepted_at: string | null;
        membership_created_at: string;
      }
  >(
    `SELECT
      integration_tokens.id AS integration_token_id,
      integration_tokens.user_id AS integration_token_user_id,
      integration_tokens.workspace_id AS integration_token_workspace_id,
      integration_tokens.name AS integration_token_name,
      integration_tokens.token_hash AS integration_token_token_hash,
      integration_tokens.token_prefix AS integration_token_token_prefix,
      integration_tokens.scope AS integration_token_scope,
      integration_tokens.last_used_at::text AS integration_token_last_used_at,
      integration_tokens.expires_at::text AS integration_token_expires_at,
      integration_tokens.revoked_at::text AS integration_token_revoked_at,
      integration_tokens.created_at AS integration_token_created_at,
      users.id AS user_id_row,
      users.email AS user_email,
      users.password_hash AS user_password_hash,
      users.name AS user_name,
      users.created_at AS user_created_at,
      workspaces.id AS workspace_id_row,
      workspaces.name AS workspace_name,
      workspaces.workspace_type AS workspace_workspace_type,
      workspaces.billing_email AS workspace_billing_email,
      workspaces.billing_plan AS workspace_billing_plan,
      workspaces.billing_interval AS workspace_billing_interval,
      workspaces.subscription_status AS workspace_subscription_status,
      workspaces.stripe_customer_id AS workspace_stripe_customer_id,
      workspaces.stripe_subscription_id AS workspace_stripe_subscription_id,
      workspaces.rescue_verification_status AS workspace_rescue_verification_status,
      workspaces.created_at AS workspace_created_at,
      workspaces.updated_at AS workspace_updated_at,
      workspace_members.id AS membership_id_row,
      workspace_members.workspace_id AS membership_workspace_id,
      workspace_members.user_id AS membership_user_id,
      COALESCE(workspace_members.invite_email, workspace_members.email) AS membership_invite_email,
      workspace_members.name AS membership_name,
      workspace_members.role AS membership_role,
      workspace_members.accepted_at::text AS membership_accepted_at,
      workspace_members.created_at AS membership_created_at
    FROM integration_tokens
    INNER JOIN users ON users.id = integration_tokens.user_id
    INNER JOIN workspaces ON workspaces.id = integration_tokens.workspace_id
    INNER JOIN workspace_members
      ON workspace_members.workspace_id = integration_tokens.workspace_id
     AND workspace_members.user_id = integration_tokens.user_id
    WHERE integration_tokens.token_hash = $1
      AND integration_tokens.revoked_at IS NULL
      AND (integration_tokens.expires_at IS NULL OR integration_tokens.expires_at > CURRENT_TIMESTAMP)`,
    [tokenHash],
  );

  if (!result.rows[0]) {
    return null;
  }

  await db.query(
    `UPDATE integration_tokens
     SET last_used_at = CURRENT_TIMESTAMP
     WHERE id = $1`,
    [result.rows[0].integration_token_id],
  );

  return mapIntegrationTokenAuthRow(result.rows[0], token);
};
