import { r as redirect } from './index-wpIsICWW.js';
import { and, eq, inArray, getTableColumns, sql, isNull, isNotNull, gt, or, between, desc } from 'drizzle-orm';
import { PlaidApi, CountryCode, Products, Configuration, PlaidEnvironments } from 'plaid';
import { d as db, u as userCard, p as plaidTransaction, a as user, A as APP_NAME, w as withPagination, P as PLAID_SECRET, b as PLAID_CLIENT_ID, N as NODE_ENV } from './index3-Cipag7JS.js';
import { s as stripe } from './index4-BWaXq8De.js';
import { r as requireUserMiddleware, v as validateBodyMiddleware } from './middleware-DEO9L-XC.js';
import { type } from 'arktype';
import { s as sendCongratulationsEmail } from './index5-BOKyATXQ.js';
import 'drizzle-orm/libsql';
import 'drizzle-orm/sqlite-core';
import 'crypto';
import 'dotenv/config.js';
import 'stripe';
import './utils2-BNPuf_o4.js';
import 'nodemailer';

const configuration = new Configuration({
  basePath: NODE_ENV === "production" ? PlaidEnvironments.production : PlaidEnvironments.sandbox,
  baseOptions: {
    headers: {
      "PLAID-CLIENT-ID": PLAID_CLIENT_ID,
      "PLAID-SECRET": PLAID_SECRET
    }
  }
});
const plaid = new PlaidApi(configuration);
async function getPlaidTransactions(accessToken, cursor) {
  let added = [];
  let modified = [];
  let removed = [];
  let hasMore = true;
  while (hasMore) {
    const request = {
      access_token: accessToken,
      cursor
    };
    const response = await plaid.transactionsSync(request);
    const data = response.data;
    added = added.concat(data.added);
    modified = modified.concat(data.modified);
    removed = removed.concat(data.removed);
    hasMore = data.has_more;
    cursor = data.next_cursor;
  }
  return {
    added,
    modified,
    removed,
    cursor
  };
}
function generateCardsQuery() {
  const startOfToday = new Date((/* @__PURE__ */ new Date()).setHours(0, 0, 0, 0));
  const endOfToday = new Date((/* @__PURE__ */ new Date()).setHours(11, 59, 59, 999));
  return db.select({
    // ...getTableColumns(userCard),
    id: userCard.id,
    nickname: userCard.nickname,
    userId: userCard.userId,
    plaidTxCursor: userCard.plaidTxCursor,
    plaidAccessToken: userCard.plaidAccessToken,
    bonusSpend: userCard.bonusSpend,
    totalSpent: sql`SUM(${plaidTransaction.amount})`.as("totalSpent")
  }).from(userCard).leftJoin(plaidTransaction, eq(userCard.id, plaidTransaction.cardId)).where(
    and(
      // Only get card that...
      isNull(userCard.closedAt),
      // is not closed
      isNotNull(userCard.plaidAccessToken),
      // has an access token
      gt(userCard.bonusDeadline, endOfToday),
      // future bonus deadline
      or(
        isNull(userCard.syncedAt),
        // never been synced
        between(userCard.syncedAt, startOfToday, endOfToday)
        // hasn't already synced today
      )
    )
  ).groupBy(userCard.id).having(
    or(
      isNull(sql`totalSpent`),
      // no transactions yet
      gt(userCard.bonusSpend, sql`totalSpent`)
      // bonus hasn't been hit
    )
  ).orderBy(desc(userCard.bonusDeadline)).$dynamic();
}
async function task(ctx) {
  const [cards, total] = await Promise.all([
    withPagination(generateCardsQuery(), { page: 1, perPage: 1 }),
    db.$count(generateCardsQuery())
  ]);
  for await (const card of cards) {
    if (!card.plaidAccessToken) break;
    const { cursor, added, modified, removed } = await getPlaidTransactions(
      card.plaidAccessToken,
      card.plaidTxCursor ?? void 0
    );
    await db.update(userCard).set({ plaidTxCursor: cursor, syncedAt: /* @__PURE__ */ new Date() }).where(eq(userCard.id, card.id));
    let totalSpent = card.totalSpent ?? 0;
    const bonusSpend = card.bonusSpend ?? 0;
    if (added.length) {
      await db.insert(plaidTransaction).values(
        added.map((tx) => {
          totalSpent += tx.amount;
          return {
            cardId: card.id,
            transaction_id: tx.transaction_id,
            amount: tx.amount,
            date: new Date(tx.date),
            pending: tx.pending,
            meta: JSON.stringify(tx)
          };
        })
      );
    }
    if (modified.length) ;
    if (removed.length) {
      await db.delete(plaidTransaction).where(
        inArray(
          userCard.plaidItemId,
          removed.map((tx) => tx.transaction_id)
        )
      );
    }
    if (totalSpent > bonusSpend) {
      const [user$1] = await db.select().from(user).where(eq(user.id, card.userId));
      if (user$1) {
        await sendCongratulationsEmail({ user: user$1, card });
      }
    }
  }
  if (total > 0) {
    task();
  }
}
const load = async (event) => {
  const user2 = requireUserMiddleware(event);
  const cardsQuery = db.select({
    ...getTableColumns(userCard),
    totalSpent: sql`SUM(${plaidTransaction.amount})`
  }).from(userCard).leftJoin(plaidTransaction, eq(userCard.id, plaidTransaction.cardId)).groupBy(userCard.id).where(eq(userCard.userId, user2.id)).orderBy(userCard.bonusDeadline);
  const plaidTokenCreate = plaid.linkTokenCreate({
    user: {
      client_user_id: user2.id
    },
    client_name: APP_NAME,
    products: [Products.Transactions],
    language: "en",
    // webhook: 'https://webhook.example.com',
    // redirect_uri: 'https://domainname.com/oauth-page.html',
    country_codes: [CountryCode.Us]
  }).catch(async (e) => {
    console.log(e);
    throw e;
  });
  const [cards, plaidResponse] = await Promise.all([cardsQuery, plaidTokenCreate]);
  return {
    cards,
    plaid: plaidResponse.data
  };
};
const actions = {
  async plaidTokenExchange(event) {
    const user$1 = requireUserMiddleware(event);
    const bodySchema = type({
      cardId: "string > 0",
      publicToken: "string > 0"
    });
    const body = await validateBodyMiddleware(bodySchema, event);
    const tokenExchangeResponse = await plaid.itemPublicTokenExchange({
      public_token: body.publicToken
    });
    const accountsResponse = await plaid.accountsGet({
      access_token: tokenExchangeResponse.data.access_token
    });
    const plaidCard = accountsResponse.data.accounts[0];
    const { cursor, added, modified, removed } = await getPlaidTransactions(
      tokenExchangeResponse.data.access_token
    );
    const [card] = await db.update(userCard).set({
      plaidName: plaidCard.name,
      plaidMask: plaidCard.mask,
      plaidAccessToken: tokenExchangeResponse.data.access_token,
      plaidItemId: tokenExchangeResponse.data.item_id,
      plaidInstitutionId: accountsResponse.data.item.institution_id,
      plaidBank: accountsResponse.data.item.institution_name,
      plaidTxCursor: cursor,
      syncedAt: /* @__PURE__ */ new Date()
    }).where(and(eq(userCard.userId, user$1.id), eq(userCard.id, body.cardId))).returning();
    let totalSpent = 0;
    const bonusSpend = card.bonusSpend ?? 0;
    const transactionQueries = [];
    if (added.length) {
      transactionQueries.push(
        db.insert(plaidTransaction).values(
          added.map((tx) => {
            totalSpent += tx.amount;
            return {
              cardId: card.id,
              transaction_id: tx.transaction_id,
              amount: tx.amount,
              date: new Date(tx.date),
              pending: tx.pending,
              meta: JSON.stringify(tx)
            };
          })
        )
      );
    }
    if (modified.length) ;
    if (removed.length) {
      transactionQueries.push(
        db.delete(plaidTransaction).where(
          inArray(
            userCard.plaidItemId,
            removed.map((tx) => tx.transaction_id)
          )
        )
      );
    }
    await Promise.all(transactionQueries);
    if (totalSpent > bonusSpend) {
      const [user2] = await db.select().from(user).where(eq(user.id, card.userId));
      if (user2) {
        await sendCongratulationsEmail({ user: user2, card });
      }
    }
    return card;
  },
  async activate(event) {
    const user2 = requireUserMiddleware(event);
    const cardId = event.url.searchParams.get("cardId");
    if (!cardId) return;
    const origin = event.request.headers.get("origin");
    const session = await stripe.checkout.sessions.create({
      customer_email: user2.email,
      line_items: [
        {
          // Use Price item ID in Stripe dashboard, or dynamic price data in code
          // price: PRICE_ID,
          price_data: {
            unit_amount: 500,
            currency: "usd",
            product_data: {
              name: "Perx Tracker"
            }
          },
          quantity: 1
        }
      ],
      mode: "payment",
      // 'subscription'
      success_url: `${origin}/thank-you?sessionId={CHECKOUT_SESSION_ID}`,
      metadata: {
        userId: user2.id,
        cardId
      }
      // cancel_url: `${origin}/checkout?status=canceled`,
      /** TODO: apply branding settings via API @see https://docs.stripe.com/api/checkout/sessions/create#create_checkout_session-branding_settings */
      // branding_settings: {},
      /** TODO: customize text @see https://docs.stripe.com/payments/checkout/customization/policies */
      // custom_text: {}
    });
    return redirect(303, session.url);
  },
  async close(event) {
    const user2 = requireUserMiddleware(event);
    const cardId = event.url.searchParams.get("cardId");
    if (!cardId) return;
    await db.update(userCard).set({
      closedAt: /* @__PURE__ */ new Date()
    }).where(and(eq(userCard.userId, user2.id), eq(userCard.id, cardId)));
  },
  sync(event) {
    task();
  }
};

var _page_server_ts = /*#__PURE__*/Object.freeze({
  __proto__: null,
  actions: actions,
  load: load
});

const index = 3;
let component_cache;
const component = async () => component_cache ??= (await import('./_page.svelte-Oi6QNSnZ.js')).default;
const server_id = "src/routes/dashboard/+page.server.ts";
const imports = ["_app/immutable/nodes/3.DH5Fg0ar.js","_app/immutable/chunks/cL9VaPgy.js","_app/immutable/chunks/C75YQU_W.js","_app/immutable/chunks/D1kbsK6n.js","_app/immutable/chunks/B4FYT0oR.js","_app/immutable/chunks/BAJkae7J.js","_app/immutable/chunks/Wy6oXzdU.js","_app/immutable/chunks/D-5ps19L.js","_app/immutable/chunks/BGk0RnQK.js","_app/immutable/chunks/Cz-4yBSw.js"];
const stylesheets = [];
const fonts = [];

export { component, fonts, imports, index, _page_server_ts as server, server_id, stylesheets };
//# sourceMappingURL=3-CgziZoLt.js.map
