import {
  sqliteTable,
  text,
  integer,
  primaryKey,
  index,
} from "drizzle-orm/sqlite-core";
import { relations, sql } from "drizzle-orm";

const id = (name = "id") =>
  text(name).primaryKey().$defaultFn(() => crypto.randomUUID());

const timestamps = {
  createdAt: integer("created_at", { mode: "timestamp_ms" })
    .notNull()
    .default(sql`(unixepoch() * 1000)`),
  updatedAt: integer("updated_at", { mode: "timestamp_ms" })
    .notNull()
    .default(sql`(unixepoch() * 1000)`),
};

export const households = sqliteTable("households", {
  id: id(),
  name: text("name").notNull(),
  baseCurrency: text("base_currency").notNull().default("GBP"),
  ...timestamps,
});

export const users = sqliteTable("users", {
  id: id(),
  name: text("name"),
  email: text("email").notNull().unique(),
  emailVerified: integer("email_verified", { mode: "timestamp_ms" }),
  image: text("image"),
  passwordHash: text("password_hash"),
  householdId: text("household_id").references(() => households.id, {
    onDelete: "set null",
  }),
  role: text("role", { enum: ["owner", "member"] }).notNull().default("member"),
  ...timestamps,
});

export const authAccounts = sqliteTable(
  "auth_accounts",
  {
    userId: text("user_id")
      .notNull()
      .references(() => users.id, { onDelete: "cascade" }),
    type: text("type").notNull(),
    provider: text("provider").notNull(),
    providerAccountId: text("provider_account_id").notNull(),
    refresh_token: text("refresh_token"),
    access_token: text("access_token"),
    expires_at: integer("expires_at"),
    token_type: text("token_type"),
    scope: text("scope"),
    id_token: text("id_token"),
    session_state: text("session_state"),
  },
  (t) => ({
    pk: primaryKey({ columns: [t.provider, t.providerAccountId] }),
  }),
);

export const sessions = sqliteTable("sessions", {
  sessionToken: text("session_token").primaryKey(),
  userId: text("user_id")
    .notNull()
    .references(() => users.id, { onDelete: "cascade" }),
  expires: integer("expires", { mode: "timestamp_ms" }).notNull(),
});

export const verificationTokens = sqliteTable(
  "verification_tokens",
  {
    identifier: text("identifier").notNull(),
    token: text("token").notNull(),
    expires: integer("expires", { mode: "timestamp_ms" }).notNull(),
  },
  (t) => ({ pk: primaryKey({ columns: [t.identifier, t.token] }) }),
);

export const bankAccounts = sqliteTable(
  "bank_accounts",
  {
    id: id(),
    householdId: text("household_id")
      .notNull()
      .references(() => households.id, { onDelete: "cascade" }),
    ownerUserId: text("owner_user_id").references(() => users.id, {
      onDelete: "set null",
    }),
    name: text("name").notNull(),
    type: text("type", {
      enum: ["current", "savings", "credit", "cash", "investment"],
    })
      .notNull()
      .default("current"),
    currency: text("currency").notNull().default("GBP"),
    openingBalanceMinor: integer("opening_balance_minor")
      .notNull()
      .default(0),
    openingDate: text("opening_date").notNull(),
    isShared: integer("is_shared", { mode: "boolean" }).notNull().default(false),
    isArchived: integer("is_archived", { mode: "boolean" })
      .notNull()
      .default(false),
    color: text("color"),
    ...timestamps,
  },
  (t) => ({
    byHousehold: index("bank_accounts_household_idx").on(t.householdId),
  }),
);

export const categories = sqliteTable(
  "categories",
  {
    id: id(),
    householdId: text("household_id")
      .notNull()
      .references(() => households.id, { onDelete: "cascade" }),
    name: text("name").notNull(),
    kind: text("kind", { enum: ["income", "expense", "transfer"] })
      .notNull()
      .default("expense"),
    color: text("color"),
    icon: text("icon"),
    ...timestamps,
  },
  (t) => ({
    byHousehold: index("categories_household_idx").on(t.householdId),
  }),
);

export const recurringRules = sqliteTable(
  "recurring_rules",
  {
    id: id(),
    householdId: text("household_id")
      .notNull()
      .references(() => households.id, { onDelete: "cascade" }),
    bankAccountId: text("bank_account_id")
      .notNull()
      .references(() => bankAccounts.id, { onDelete: "cascade" }),
    categoryId: text("category_id").references(() => categories.id, {
      onDelete: "set null",
    }),
    description: text("description").notNull(),
    amountMinor: integer("amount_minor").notNull(),
    rrule: text("rrule").notNull(),
    startDate: text("start_date").notNull(),
    endDate: text("end_date"),
    isActive: integer("is_active", { mode: "boolean" })
      .notNull()
      .default(true),
    ...timestamps,
  },
  (t) => ({
    byHousehold: index("recurring_rules_household_idx").on(t.householdId),
    byAccount: index("recurring_rules_account_idx").on(t.bankAccountId),
  }),
);

export const transactions = sqliteTable(
  "transactions",
  {
    id: id(),
    householdId: text("household_id")
      .notNull()
      .references(() => households.id, { onDelete: "cascade" }),
    bankAccountId: text("bank_account_id")
      .notNull()
      .references(() => bankAccounts.id, { onDelete: "cascade" }),
    categoryId: text("category_id").references(() => categories.id, {
      onDelete: "set null",
    }),
    recurringRuleId: text("recurring_rule_id").references(
      () => recurringRules.id,
      { onDelete: "set null" },
    ),
    date: text("date").notNull(),
    amountMinor: integer("amount_minor").notNull(),
    description: text("description").notNull(),
    notes: text("notes"),
    status: text("status", {
      enum: ["posted", "projected", "cancelled"],
    })
      .notNull()
      .default("posted"),
    source: text("source", {
      enum: ["manual", "recurring", "import", "bank_sync"],
    })
      .notNull()
      .default("manual"),
    externalId: text("external_id"),
    createdByUserId: text("created_by_user_id").references(() => users.id, {
      onDelete: "set null",
    }),
    ...timestamps,
  },
  (t) => ({
    byHouseholdDate: index("transactions_household_date_idx").on(
      t.householdId,
      t.date,
    ),
    byAccountDate: index("transactions_account_date_idx").on(
      t.bankAccountId,
      t.date,
    ),
    byRule: index("transactions_rule_idx").on(t.recurringRuleId),
    byExternal: index("transactions_external_idx").on(t.externalId),
  }),
);

export const budgets = sqliteTable(
  "budgets",
  {
    id: id(),
    householdId: text("household_id")
      .notNull()
      .references(() => households.id, { onDelete: "cascade" }),
    categoryId: text("category_id")
      .notNull()
      .references(() => categories.id, { onDelete: "cascade" }),
    period: text("period", { enum: ["weekly", "monthly", "yearly"] })
      .notNull()
      .default("monthly"),
    amountMinor: integer("amount_minor").notNull(),
    startDate: text("start_date").notNull(),
    ...timestamps,
  },
  (t) => ({
    byHousehold: index("budgets_household_idx").on(t.householdId),
  }),
);

export const savingsGoals = sqliteTable(
  "savings_goals",
  {
    id: id(),
    householdId: text("household_id")
      .notNull()
      .references(() => households.id, { onDelete: "cascade" }),
    bankAccountId: text("bank_account_id").references(() => bankAccounts.id, {
      onDelete: "set null",
    }),
    name: text("name").notNull(),
    targetAmountMinor: integer("target_amount_minor").notNull(),
    currentAmountMinor: integer("current_amount_minor").notNull().default(0),
    targetDate: text("target_date"),
    monthlyContributionMinor: integer("monthly_contribution_minor")
      .notNull()
      .default(0),
    annualInterestRate: integer("annual_interest_rate_bp")
      .notNull()
      .default(0),
    ...timestamps,
  },
  (t) => ({
    byHousehold: index("goals_household_idx").on(t.householdId),
  }),
);

export const splits = sqliteTable(
  "splits",
  {
    id: id(),
    transactionId: text("transaction_id")
      .notNull()
      .references(() => transactions.id, { onDelete: "cascade" }),
    userId: text("user_id")
      .notNull()
      .references(() => users.id, { onDelete: "cascade" }),
    shareMinor: integer("share_minor").notNull(),
  },
  (t) => ({
    byTransaction: index("splits_transaction_idx").on(t.transactionId),
  }),
);

export const bankConnections = sqliteTable("bank_connections", {
  id: id(),
  householdId: text("household_id")
    .notNull()
    .references(() => households.id, { onDelete: "cascade" }),
  provider: text("provider", {
    enum: ["gocardless", "truelayer", "plaid"],
  }).notNull(),
  providerInstitutionId: text("provider_institution_id"),
  status: text("status", {
    enum: ["connected", "expired", "error", "pending"],
  })
    .notNull()
    .default("pending"),
  accessTokenEncrypted: text("access_token_encrypted"),
  refreshTokenEncrypted: text("refresh_token_encrypted"),
  expiresAt: integer("expires_at", { mode: "timestamp_ms" }),
  ...timestamps,
});

export const householdsRelations = relations(households, ({ many }) => ({
  users: many(users),
  bankAccounts: many(bankAccounts),
  categories: many(categories),
  transactions: many(transactions),
  recurringRules: many(recurringRules),
  budgets: many(budgets),
  savingsGoals: many(savingsGoals),
}));

export const usersRelations = relations(users, ({ one, many }) => ({
  household: one(households, {
    fields: [users.householdId],
    references: [households.id],
  }),
  ownedAccounts: many(bankAccounts),
}));

export const bankAccountsRelations = relations(bankAccounts, ({ one, many }) => ({
  household: one(households, {
    fields: [bankAccounts.householdId],
    references: [households.id],
  }),
  owner: one(users, {
    fields: [bankAccounts.ownerUserId],
    references: [users.id],
  }),
  transactions: many(transactions),
}));

export const transactionsRelations = relations(transactions, ({ one, many }) => ({
  household: one(households, {
    fields: [transactions.householdId],
    references: [households.id],
  }),
  bankAccount: one(bankAccounts, {
    fields: [transactions.bankAccountId],
    references: [bankAccounts.id],
  }),
  category: one(categories, {
    fields: [transactions.categoryId],
    references: [categories.id],
  }),
  recurringRule: one(recurringRules, {
    fields: [transactions.recurringRuleId],
    references: [recurringRules.id],
  }),
  splits: many(splits),
}));

export const recurringRulesRelations = relations(recurringRules, ({ one }) => ({
  bankAccount: one(bankAccounts, {
    fields: [recurringRules.bankAccountId],
    references: [bankAccounts.id],
  }),
  category: one(categories, {
    fields: [recurringRules.categoryId],
    references: [categories.id],
  }),
}));

export type Household = typeof households.$inferSelect;
export type User = typeof users.$inferSelect;
export type BankAccount = typeof bankAccounts.$inferSelect;
export type NewBankAccount = typeof bankAccounts.$inferInsert;
export type Category = typeof categories.$inferSelect;
export type Transaction = typeof transactions.$inferSelect;
export type NewTransaction = typeof transactions.$inferInsert;
export type RecurringRule = typeof recurringRules.$inferSelect;
export type NewRecurringRule = typeof recurringRules.$inferInsert;
export type Budget = typeof budgets.$inferSelect;
export type SavingsGoal = typeof savingsGoals.$inferSelect;
