class Demo::Generator
  COLORS = %w[#e99537 #4da568 #6471eb #db5a54 #df4e92 #c44fe9 #eb5429 #61c9ea #805dee #6ad28a]

  # Builds a semi-realistic mirror of what production data might look like
  def reset_and_clear_data!(family_names, require_onboarding: false)
    puts "Clearing existing data..."

    destroy_everything!

    puts "Data cleared"

    family_names.each_with_index do |family_name, index|
      create_family_and_user!(family_name, "user#{index == 0 ? "" : index + 1}@maybe.local", require_onboarding: require_onboarding)
    end

    puts "Users reset"
  end

  def reset_data!(family_names)
    puts "Clearing existing data..."

    destroy_everything!

    puts "Data cleared"

    family_names.each_with_index do |family_name, index|
      create_family_and_user!(family_name, "user#{index == 0 ? "" : index + 1}@maybe.local")
    end

    puts "Users reset"

    load_securities!

    puts "Securities loaded"

    family_names.each do |family_name|
      family = Family.find_by(name: family_name)

      ActiveRecord::Base.transaction do
        create_tags!(family)
        create_categories!(family)
        create_merchants!(family)
        create_rules!(family)
        puts "tags, categories, merchants created for #{family_name}"

        create_credit_card_account!(family)
        create_checking_account!(family)
        create_savings_account!(family)

        create_investment_account!(family)
        create_house_and_mortgage!(family)
        create_car_and_loan!(family)
        create_other_accounts!(family)

        create_transfer_transactions!(family)
      end

      puts "accounts created for #{family_name}"
    end

    puts "Demo data loaded successfully!"
  end

  def generate_basic_budget_data!(family_names)
    puts "Clearing existing data..."

    destroy_everything!

    puts "Data cleared"

    family_names.each_with_index do |family_name, index|
      create_family_and_user!(family_name, "user#{index == 0 ? "" : index + 1}@maybe.local")
    end

    puts "Users reset"

    family_names.each do |family_name|
      family = Family.find_by(name: family_name)

      ActiveRecord::Base.transaction do
        # Create parent categories
        food = family.categories.create!(name: "Food & Drink", color: COLORS.sample, classification: "expense")
        transport = family.categories.create!(name: "Transportation", color: COLORS.sample, classification: "expense")

        # Create subcategory
        restaurants = family.categories.create!(name: "Restaurants", parent: food, color: COLORS.sample, classification: "expense")

        # Create checking account
        checking = family.accounts.create!(
          accountable: Depository.new,
          name: "Demo Checking",
          balance: 3000,
          currency: "USD"
        )

        # Create one transaction for each category
        create_transaction!(account: checking, amount: 100, name: "Grocery Store", category: food, date: 2.days.ago)
        create_transaction!(account: checking, amount: 50, name: "Restaurant Meal", category: restaurants, date: 1.day.ago)
        create_transaction!(account: checking, amount: 20, name: "Gas Station", category: transport, date: Date.current)
      end

      puts "Basic budget data created for #{family_name}"
    end

    puts "Demo data loaded successfully!"
  end

  def generate_multi_currency_data!(family_names)
    puts "Clearing existing data..."

    destroy_everything!

    puts "Data cleared"

    family_names.each_with_index do |family_name, index|
      create_family_and_user!(family_name, "user#{index == 0 ? "" : index + 1}@maybe.local", currency: "EUR")
    end

    puts "Users reset"

    family_names.each do |family_name|
      puts "Generating demo data for #{family_name}"
      family = Family.find_by(name: family_name)

      usd_checking = family.accounts.create!(name: "USD Checking", currency: "USD", balance: 10000, accountable: Depository.new)
      eur_checking = family.accounts.create!(name: "EUR Checking", currency: "EUR", balance: 4900, accountable: Depository.new)
      eur_credit_card = family.accounts.create!(name: "EUR Credit Card", currency: "EUR", balance: 2300, accountable: CreditCard.new)

      create_transaction!(account: eur_credit_card, amount: 1000, currency: "EUR", name: "EUR cc expense 1")
      create_transaction!(account: eur_credit_card, amount: 1000, currency: "EUR", name: "EUR cc expense 2")
      create_transaction!(account: eur_credit_card, amount: 300, currency: "EUR", name: "EUR cc expense 3")

      create_transaction!(account: usd_checking, amount: -11000, currency: "USD", name: "USD income Transaction")
      create_transaction!(account: usd_checking, amount: 1000, currency: "USD", name: "USD expense Transaction")
      create_transaction!(account: usd_checking, amount: 1000, currency: "USD", name: "USD expense Transaction")
      create_transaction!(account: eur_checking, amount: -5000, currency: "EUR", name: "EUR income Transaction")
      create_transaction!(account: eur_checking, amount: 100, currency: "EUR", name: "EUR expense Transaction")

      puts "Transactions created for #{family_name}"
    end

    puts "Demo data loaded successfully!"
  end

  private
    def destroy_everything!
      Family.destroy_all
      Setting.destroy_all
      InviteCode.destroy_all
      ExchangeRate.destroy_all
      Security.destroy_all
      Security::Price.destroy_all
    end

    def create_family_and_user!(family_name, user_email, currency: "USD", require_onboarding: false)
      base_uuid = "d99e3c6e-d513-4452-8f24-dc263f8528c0"
      id = Digest::UUID.uuid_v5(base_uuid, family_name)

      family = Family.create!(
        id: id,
        name: family_name,
        currency: currency,
        locale: "en",
        country: "US",
        timezone: "America/New_York",
        date_format: "%m-%d-%Y"
      )

      family.start_subscription!("sub_1234567890")

      family.users.create! \
        email: user_email,
        first_name: "Demo",
        last_name: "User",
        role: "admin",
        password: "password",
        onboarded_at: require_onboarding ? nil : Time.current

      family.users.create! \
        email: "member_#{user_email}",
        first_name: "Demo (member user)",
        last_name: "User",
        role: "member",
        password: "password",
        onboarded_at: require_onboarding ? nil : Time.current
    end

    def create_rules!(family)
      family.rules.create!(
        effective_date: 1.year.ago.to_date,
        active: true,
        resource_type: "transaction",
        conditions: [
          Rule::Condition.new(condition_type: "transaction_merchant", operator: "=", value: "Whole Foods")
        ],
        actions: [
          Rule::Action.new(action_type: "set_transaction_category", value: "Groceries")
        ]
      )
    end

    def create_tags!(family)
      [ "Trips", "Emergency Fund", "Demo Tag" ].each do |tag|
        family.tags.create!(name: tag)
      end
    end

    def create_categories!(family)
      family.categories.bootstrap!

      food = family.categories.find_by(name: "Food & Drink")
      family.categories.create!(name: "Restaurants", parent: food, color: COLORS.sample, lucide_icon: "utensils", classification: "expense")
      family.categories.create!(name: "Groceries", parent: food, color: COLORS.sample, lucide_icon: "shopping-cart", classification: "expense")
      family.categories.create!(name: "Alcohol & Bars", parent: food, color: COLORS.sample, lucide_icon: "beer", classification: "expense")
    end

    def create_merchants!(family)
      merchants = [ "Amazon", "Starbucks", "McDonald's", "Target", "Costco",
                   "Home Depot", "Shell", "Whole Foods", "Walgreens", "Nike",
                   "Uber", "Netflix", "Spotify", "Delta Airlines", "Airbnb", "Sephora" ]

      merchants.each do |merchant|
        FamilyMerchant.create!(name: merchant, family: family, color: COLORS.sample)
      end
    end

    def create_credit_card_account!(family)
      cc = family.accounts.create! \
        accountable: CreditCard.new,
        name: "Chase Credit Card",
        balance: 2300,
        currency: "USD"

      800.times do
        merchant = random_family_record(Merchant, family)
        create_transaction! \
          account: cc,
          name: merchant.name,
          amount: Faker::Number.positive(to: 200),
          tags: [ tag_for_merchant(merchant, family) ],
          category: category_for_merchant(merchant, family),
          merchant: merchant
      end

      24.times do
        create_transaction! \
          account: cc,
          amount: Faker::Number.negative(from: -1000),
          name: "CC Payment"
      end
    end

    def create_checking_account!(family)
      checking = family.accounts.create! \
        accountable: Depository.new,
        name: "Chase Checking",
        balance: 15000,
        currency: "USD"

      # First create income transactions to ensure positive balance
      50.times do
        create_transaction! \
          account: checking,
          amount: Faker::Number.negative(from: -2000, to: -500),
          name: "Income",
          category: family.categories.find_by(name: "Income")
      end

      # Then create expenses that won't exceed the income
      200.times do
        create_transaction! \
          account: checking,
          name: "Expense",
          amount: Faker::Number.positive(from: 8, to: 500)
      end
    end

    def create_savings_account!(family)
      savings = family.accounts.create! \
        accountable: Depository.new,
        name: "Demo Savings",
        balance: 40000,
        currency: "USD",
        subtype: "savings"

      # Create larger income deposits first
      100.times do
        create_transaction! \
          account: savings,
          amount: Faker::Number.negative(from: -3000, to: -1000),
          tags: [ family.tags.find_by(name: "Emergency Fund") ],
          category: family.categories.find_by(name: "Income"),
          name: "Income"
      end

      # Add some smaller withdrawals that won't exceed the deposits
      50.times do
        create_transaction! \
          account: savings,
          amount: Faker::Number.positive(from: 100, to: 1000),
          name: "Savings Withdrawal"
      end
    end

    def create_transfer_transactions!(family)
      checking = family.accounts.find_by(name: "Chase Checking")
      credit_card = family.accounts.find_by(name: "Chase Credit Card")
      investment = family.accounts.find_by(name: "Robinhood")

      create_transaction!(
        account: checking,
        date: 1.day.ago.to_date,
        amount: 100,
        name: "Credit Card Payment"
      )

      create_transaction!(
        account: credit_card,
        date: 1.day.ago.to_date,
        amount: -100,
        name: "Credit Card Payment"
      )

      create_transaction!(
        account: checking,
        date: 3.days.ago.to_date,
        amount: 500,
        name: "Transfer to investment"
      )

      create_transaction!(
        account: investment,
        date: 2.days.ago.to_date,
        amount: -500,
        name: "Transfer from checking"
      )
    end

    def load_securities!
      # Create an unknown security to simulate edge cases
      Security.create! ticker: "UNKNOWN", name: "Unknown Demo Stock"

      securities = [
        { ticker: "AAPL", exchange_mic: "XNGS", exchange_operating_mic: "XNAS", name: "Apple Inc.", reference_price: 210 },
        { ticker: "TM", exchange_mic: "XNYS", exchange_operating_mic: "XNYS", name: "Toyota Motor Corporation", reference_price: 202 },
        { ticker: "MSFT", exchange_mic: "XNGS", exchange_operating_mic: "XNAS", name: "Microsoft Corporation", reference_price: 455 }
      ]

      securities.each do |security_attributes|
        security = Security.create! security_attributes.except(:reference_price)

        # Load prices for last 2 years
        (730.days.ago.to_date..Date.current).each do |date|
          reference = security_attributes[:reference_price]
          low_price = reference - 20
          high_price = reference + 20
          Security::Price.create! \
            security: security,
            date: date,
            price: Faker::Number.positive(from: low_price, to: high_price)
        end
      end
    end

    def create_investment_account!(family)
      account = family.accounts.create! \
        accountable: Investment.new,
        name: "Robinhood",
        balance: 100000,
        currency: "USD"

      aapl = Security.find_by(ticker: "AAPL")
      tm = Security.find_by(ticker: "TM")
      msft = Security.find_by(ticker: "MSFT")
      unknown = Security.find_by(ticker: "UNKNOWN")

      # Buy 20 shares of the unknown stock to simulate a stock where we can't fetch security prices
      account.entries.create! date: 10.days.ago.to_date, amount: 100, currency: "USD", name: "Buy unknown stock", entryable: Trade.new(qty: 20, price: 5, security: unknown, currency: "USD")

      trades = [
        { security: aapl, qty: 20 }, { security: msft, qty: 10 }, { security: aapl, qty: -5 },
        { security: msft, qty: -5 }, { security: tm, qty: 10 }, { security: msft, qty: 5 },
        { security: tm, qty: 10 }, { security: aapl, qty: -5 }, { security: msft, qty: -5 },
        { security: tm, qty: 10 }, { security: msft, qty: 5 }, { security: aapl, qty: -10 }
      ]

      trades.each do |trade|
        date = Faker::Number.positive(to: 730).days.ago.to_date
        security = trade[:security]
        qty = trade[:qty]
        price = Security::Price.find_by(security: security, date: date)&.price || 1
        name_prefix = qty < 0 ? "Sell " : "Buy "

        account.entries.create! \
          date: date,
          amount: qty * price,
          currency: "USD",
          name: name_prefix + "#{qty} shares of #{security.ticker}",
          entryable: Trade.new(qty: qty, price: price, currency: "USD", security: security)
      end
    end

    def create_house_and_mortgage!(family)
      house = family.accounts.create! \
        accountable: Property.new,
        name: "123 Maybe Way",
        balance: 560000,
        currency: "USD"

      create_valuation!(house, 3.years.ago.to_date, 520000)
      create_valuation!(house, 2.years.ago.to_date, 540000)
      create_valuation!(house, 1.years.ago.to_date, 550000)

      mortgage = family.accounts.create! \
        accountable: Loan.new,
        name: "Mortgage",
        balance: 495000,
        currency: "USD"

      create_valuation!(mortgage, 3.years.ago.to_date, 495000)
      create_valuation!(mortgage, 2.years.ago.to_date, 490000)
      create_valuation!(mortgage, 1.years.ago.to_date, 485000)
    end

    def create_car_and_loan!(family)
      vehicle = family.accounts.create! \
        accountable: Vehicle.new,
        name: "Honda Accord",
        balance: 18000,
        currency: "USD"

      create_valuation!(vehicle, 1.year.ago.to_date, 18000)

      loan = family.accounts.create! \
        accountable: Loan.new,
        name: "Car Loan",
        balance: 8000,
        currency: "USD"

      create_valuation!(loan, 1.year.ago.to_date, 8000)
    end

    def create_other_accounts!(family)
      other_asset = family.accounts.create! \
        accountable: OtherAsset.new,
        name: "Other Asset",
        balance: 10000,
        currency: "USD"

      other_liability = family.accounts.create! \
        accountable: OtherLiability.new,
        name: "Other Liability",
        balance: 5000,
        currency: "USD"

      create_valuation!(other_asset, 1.year.ago.to_date, 10000)
      create_valuation!(other_liability, 1.year.ago.to_date, 5000)
    end

    def create_transaction!(attributes = {})
      entry_attributes = attributes.except(:category, :tags, :merchant)
      transaction_attributes = attributes.slice(:category, :tags, :merchant)

      entry_defaults = {
        date: Faker::Number.between(from: 0, to: 730).days.ago.to_date,
        currency: "USD",
        entryable: Transaction.new(transaction_attributes)
      }

      Entry.create! entry_defaults.merge(entry_attributes)
    end

    def create_valuation!(account, date, amount)
      Entry.create! \
        account: account,
        date: date,
        amount: amount,
        currency: "USD",
        name: "Balance update",
        entryable: Valuation.new
    end

    def random_family_record(model, family)
      family_records = model.where(family_id: family.id)
      model.offset(rand(family_records.count)).first
    end

    def category_for_merchant(merchant, family)
      mapping = {
        "Amazon" => "Shopping",
        "Starbucks" => "Food & Drink",
        "McDonald's" => "Food & Drink",
        "Target" => "Shopping",
        "Costco" => "Food & Drink",
        "Home Depot" => "Housing",
        "Shell" => "Transportation",
        "Whole Foods" => "Food & Drink",
        "Walgreens" => "Healthcare",
        "Nike" => "Shopping",
        "Uber" => "Transportation",
        "Netflix" => "Subscriptions",
        "Spotify" => "Subscriptions",
        "Delta Airlines" => "Transportation",
        "Airbnb" => "Housing",
        "Sephora" => "Shopping"
      }

      family.categories.find_by(name: mapping[merchant.name])
    end

    def tag_for_merchant(merchant, family)
      mapping = {
        "Delta Airlines" => "Trips",
        "Airbnb" => "Trips"
      }

      tag_from_merchant = family.tags.find_by(name: mapping[merchant.name])
      tag_from_merchant || family.tags.find_by(name: "Demo Tag")
    end

    def securities
      @securities ||= Security.all.to_a
    end
end
