#include "chance.h"
#include "community_chest.h"
#include "monopoly_board.h"
#include "player.h"
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

// Forward Declarations
void handle_roll(Player *p, bool *has_rolled, int *double_rolls);
void handle_buy(Player *p, bool has_rolled);
void handle_trade(Player *p);
void handle_build(Player *p);
void process_landing(Player *p);
void handle_property_landing(Player *p, const BoardTile *tile);
void handle_chance(Player *p);
void find_properties_on_group_from_position(int position,
                                            int positions_found[4], int *count);
void handle_community_chest(Player *p);
void handle_jail_options(Player *p);
bool attempt_jail_escape(Player *p);

// Global State
Player players[MAX_PLAYERS];
int players_count = 2;

int current_player_idx = 0;
bool just_sent_to_jail = false; // Flag to end turn when sent to jail

int main() {
  srand(time(NULL));

  // Initialize hardcoded players
  init_player(&players[0], 0, "Elias", "Top Hat");
  init_player(&players[1], 1, "Adolfo", "Racecar");
  init_player(&players[2], 2, "Grace", "Thimble");
  init_player(&players[3], 3, "Alicia", "Dog");

  printf("\n╔════════════════════════════════════════╗\n");
  printf("║     Welcome to C-Monopoly!            ║\n");
  printf("╚════════════════════════════════════════╝\n\n");

  bool running = true;
  bool has_rolled = false; // Turn state tracker
  int double_rolls = 0;
  current_player_idx = 0;
  while (running) {
    Player *p = &players[current_player_idx];
    printf("\n╔═════════════════════════════════════════╗\n");
    printf("║ %s's Turn (%s)\n", p->name, p->token);
    printf("║ 💰 Balance: $%d | 📍 %s\n", p->balance,
           MONOPOLY_BOARD[p->position].name);
    printf("╚═════════════════════════════════════════╝\n\n");
    // Handle jail status at start of turn
    if (p->is_in_jail) {
      if (just_sent_to_jail) {
        // Player was just sent to jail this turn, end their turn
        printf("\n🚨 You have been sent to Jail! Your turn ends.\n");
        current_player_idx = (current_player_idx + 1) % players_count;
        has_rolled = false;
        just_sent_to_jail = false;
        continue;
      }

      printf("\n🚨 YOU ARE IN JAIL!\n");
      printf("   Turns in jail: %d/3\n", p->jail_turns);
      handle_jail_options(p);

      if (!p->is_in_jail) {
        // Player escaped jail, now they can roll
        printf("\n📋 %s escaped jail!\n\n", p->name);
      } else {
        // Player remains in jail, end turn
        printf("\n📋 %s remains in jail.\n", p->name);
        current_player_idx = (current_player_idx + 1) % players_count;
        has_rolled = false;
        continue;
      }
    }
    if (!has_rolled)
      printf("⚠️  You must roll the dice first!\n\n");

    printf("╔─ Available Actions ──────────────────────╗\n");
    printf("║ 1. 🎲 Roll Dice\n");
    printf("║ 2. 🏠 Buy Property\n");
    printf("║ 3. 🏗️  Build Houses/Hotels\n");
    printf("║ 4. 🤝 Trade (Empty)\n");
    printf("║ 5. ⏭️  End Turn\n");
    printf("║ 6. ❌ Exit Game\n");
    printf("╚═════════════════════════════════════════╝\n");
    printf("\nChoose action (1-6): ");

    int choice;
    scanf("%d", &choice);

    switch (choice) {
    case 1:
      handle_roll(p, &has_rolled, &double_rolls);
      break;
    case 2:
      handle_buy(p, has_rolled);
      break;
    case 3:
      handle_build(p);
      break;
    case 4:
      handle_trade(p);
      break;
    case 5:
      if (has_rolled) {
        current_player_idx = (current_player_idx + 1) % players_count;
        has_rolled = false;        // Reset for next player
        just_sent_to_jail = false; // Reset jail flag
        printf("✓ Turn ended.\n");
      } else {
        printf("❌ You must roll before ending your turn!\n");
      }
      break;
    case 6:
      running = false;
      break;
    default:
      printf("❌ Invalid choice! Please enter 1-6.\n");
    }
  }

  return 0;
}

void find_properties_on_group_from_position(int position,
                                            int positions_found[4],
                                            int *count) {
  const BoardTile *tile = &MONOPOLY_BOARD[position];
  if (tile->type == TILE_PROPERTY || tile->type == TILE_RAILROAD ||
      tile->type == TILE_UTILITY) {
    int group_id = tile->group[0];
    *count = 0;
    for (int i = 0; i < MONOPOLY_BOARD_SIZE; i++) {
      if (i == position)
        continue;
      const BoardTile *t = &MONOPOLY_BOARD[i];
      if (t->group[0] == group_id) {
        positions_found[(*count)++] = i;
      }
    }
  }
}

bool handle_if_owned(Player *p, const BoardTile *tile) {
  if (tile->type == TILE_PROPERTY || tile->type == TILE_RAILROAD ||
      tile->type == TILE_UTILITY) {
    bool is_owned = false;
    int i = 0;
    for (i = 0; i < MAX_PLAYERS; i++) {
      for (int j = 0; j < players[i].property_count; j++) {
        if (players[i].properties_owned[j] == p->position) {
          is_owned = true;
          printf("🏠 This property is owned by %s.\n", players[i].name);
          break;
        }
      }
      if (is_owned)
        break;
    }
    if (is_owned) {
      if (players[i].id == p->id) {
        printf("✓ You own this property.\n");
        return true;
      }
      // pay rent logic would go here
      p->balance -= tile->rent[0]; // Simplistic: always pay base rent
      players[i].balance += tile->rent[0];
      printf("💰 Paid $%d in rent to the owner.\n", tile->rent[0]);
      return true;
    }
  }
  return false;
}

void handle_roll(Player *p, bool *has_rolled, int *double_rolls) {
  if (*has_rolled) {
    printf("❌ You have already moved this turn!\n");
    return;
  }

  // Check if player is in jail and trying to escape
  if (p->is_in_jail) {
    printf("❌ You are in jail! Use the jail menu to escape first.\n");
    return;
  }

  int dice1 = (rand() % 6) + 1;
  int dice2 = (rand() % 6) + 1;
  printf("\n🎲 Rolled: [%d] + [%d] = %d\n", dice1, dice2, dice1 + dice2);
  int total = dice1 + dice2;
  p->position = (p->position + total) % MONOPOLY_BOARD_SIZE;

  if (dice1 == dice2) {
    (*double_rolls)++;
    if (*double_rolls >= 3) {
      printf("🚨 Three doubles in a row! Sent directly to JAIL!\n");
      p->position = 10; // Jail position
      p->jail_turns = 0;
      p->is_in_jail = true;
      *double_rolls = 0;
      *has_rolled = true;
      just_sent_to_jail = true;
      return;
    } else {
      printf("✨ Doubles! You get another turn!\n");
    }
  } else {
    *double_rolls = 0; // Reset double rolls count
  }

  *has_rolled = true;

  // Check for passing GO
  if (p->position < (p->position - total)) { // Simplistic wrap-around check
    p->balance += 200;
    printf("Passed GO! Collected $200.\n");
  }

  printf("➜ Moved %d spaces to: %s\n\n", total,
         MONOPOLY_BOARD[p->position].name);
  process_landing(p);
}

void handle_buy(Player *p, bool has_rolled) {
  if (!has_rolled) {
    printf("❌ You can't buy anything until you roll and land on a tile!\n");
    return;
  }

  const BoardTile *tile = &MONOPOLY_BOARD[p->position];

  // Check if it's even a purchasable type
  if (tile->type != TILE_PROPERTY && tile->type != TILE_RAILROAD &&
      tile->type != TILE_UTILITY) {
    printf("❌ This location (%s) cannot be purchased.\n", tile->name);
    return;
  }

  // Check if someone already owns it (basic check)
  // In a full game, we'd iterate through all players' properties_owned arrays
  for (int i = 0; i < MAX_PLAYERS; i++) {
    for (int j = 0; j < players[i].property_count; j++) {
      if (players[i].properties_owned[j] == p->position) {
        printf("❌ This property is already owned by %s.\n", players[i].name);
        return;
      }
    }
  }

  if (p->balance >= tile->cost) {
    p->balance -= tile->cost;
    p->properties_owned[p->property_count++] = p->position;
    printf("✓ Bought %s for $%d! (Balance: $%d)\n", tile->name, tile->cost,
           p->balance);
  } else {
    printf("❌ Insufficient funds! Cost: $%d | Your Balance: $%d\n", tile->cost,
           p->balance);
  }
}

void process_landing(Player *p) {
  const BoardTile *tile = &MONOPOLY_BOARD[p->position];

  printf("\n─────────────────────────────────────────\n");
  printf("📍 Landed on: %s\n", tile->name);
  printf("─────────────────────────────────────────\n");

  switch (tile->type) {
  case TILE_PROPERTY:
  case TILE_RAILROAD:
  case TILE_UTILITY:
    handle_property_landing(p, tile);
    break;

  case TILE_TAX:
    p->balance -= tile->cost;
    printf("💸 Tax payment: $%d (Balance: $%d)\n", tile->cost, p->balance);
    break;

  case TILE_CHANCE:
    printf("\n⚡ CHANCE CARD! ⚡\n");
    handle_chance(p);
    break;

  case TILE_COMMUNITY_CHEST:
    printf("\n📦 COMMUNITY CHEST CARD! 📦\n");
    handle_community_chest(p);
    break;

  case TILE_FREE_PARKING:
  case TILE_GO:
  case TILE_JAIL:
    // Corner tiles (GO, Free Parking, Go to Jail, Just Visiting)
    printf("📌 You're at: %s\n", tile->name);
    break;

  default:
    break;
  }
}

void handle_property_landing(Player *p, const BoardTile *tile) {
  bool is_owned = handle_if_owned(p, tile);
  if (is_owned) {
    return;
  }

  printf("\n💰 Purchase Price: $%d\n", tile->cost);
  if (tile->type == TILE_PROPERTY) {
    // rent data
    printf("\n📋 Rent Schedule:\n");
    printf("  Vacant.....................$%d\n", tile->rent[0]);
    printf("  1 House.....................$%d\n", tile->rent[1]);
    printf("  2 Houses.....................$%d\n", tile->rent[2]);
    printf("  3 Houses.....................$%d\n", tile->rent[3]);
    printf("  4 Houses.....................$%d\n", tile->rent[4]);
    printf("  Hotel.......................$%d\n", tile->rent[5]);
    printf("\n🏗️  House Cost: $%d\n", tile->house_cost);
  }

  // show if the other properties in the group are owned
  printf("\n🏘️  Property Group: %d | Position %d/%d in group\n", tile->group[0],
         tile->group[1], tile->group[2]);
  int group_positions[4];
  int group_count = 0;
  find_properties_on_group_from_position(p->position, group_positions,
                                         &group_count);
  if (group_count > 0) {
    printf("\n  Related properties:\n");
    for (int i = 0; i < group_count; i++) {
      const BoardTile *gtile = &MONOPOLY_BOARD[group_positions[i]];
      // Check if owned by any player
      bool is_owned_in_group = false;
      for (int j = 0; j < MAX_PLAYERS; j++) {
        for (int k = 0; k < players[j].property_count; k++) {
          if (players[j].properties_owned[k] == group_positions[i]) {
            is_owned_in_group = true;
            printf("    🔒 %s (Owned by %s)\n", gtile->name, players[j].name);
            break;
          }
        }
        if (is_owned_in_group)
          break;
      }
      if (!is_owned_in_group) {
        printf("    🔓 %s - $%d (available)\n", gtile->name, gtile->cost);
      }
    }
  }
}

void handle_trade(Player *p) {
  printf("⚙️  Trade functionality is not yet implemented.\n");
}

void handle_jail_options(Player *p) {
  bool escaped = false;

  printf("\n╔─ Jail Options ──────────────────────────────╗\n");
  printf("║ 1. 🎲 Roll Doubles to Escape\n");
  printf("║ 2. 💰 Pay $50 Bail\n");
  if (p->jail_free_cards > 0)
    printf("║ 3. 🔑 Use Get Out of Jail Free Card\n");
  printf("╚─────────────────────────────────────────────╝\n");
  printf("Choose option: ");

  int choice;
  scanf("%d", &choice);

  switch (choice) {
  case 1:
    // Try to roll doubles
    escaped = attempt_jail_escape(p);
    break;
  case 2:
    // Pay bail
    if (p->balance >= 50) {
      p->balance -= 50;
      p->is_in_jail = false;
      p->jail_turns = 0;
      escaped = true;
      printf("\n✓ Paid $50 bail! You are now free.\n");
    } else {
      printf("\n❌ Insufficient funds! You need $50.\n");
    }
    break;
  case 3:
    if (p->jail_free_cards > 0) {
      p->jail_free_cards--;
      p->is_in_jail = false;
      p->jail_turns = 0;
      escaped = true;
      printf("\n✓ Used a Get Out of Jail Free card! You are now free.\n");
      printf("   Cards remaining: %d\n", p->jail_free_cards);
    } else {
      printf("\n❌ You don't have any Get Out of Jail Free cards!\n");
    }
    break;
  default:
    printf("\n❌ Invalid choice!\n");
    break;
  }

  if (!escaped) {
    p->jail_turns++;
    if (p->jail_turns >= 3) {
      // Force payment after 3 turns
      printf("\n⏰ You've been in jail for 3 turns. You must pay $50 bail!\n");
      p->balance -= 50;
      p->is_in_jail = false;
      p->jail_turns = 0;
      printf("   Balance: $%d\n", p->balance);
    }
  }
}

bool attempt_jail_escape(Player *p) {
  int dice1 = (rand() % 6) + 1;
  int dice2 = (rand() % 6) + 1;
  printf("\n🎲 Rolling to escape...\n");
  printf("   Rolled: [%d] + [%d]\n", dice1, dice2);

  if (dice1 == dice2) {
    printf("\n✨ DOUBLES! You escaped jail!\n");
    p->is_in_jail = false;
    p->jail_turns = 0;
    return true;
  } else {
    printf("\n❌ No doubles. You remain in jail.\n");
    p->jail_turns++;
    if (p->jail_turns >= 3) {
      printf("\n⏰ You've been in jail for 3 turns. You must pay $50 bail!\n");
      p->balance -= 50;
      p->is_in_jail = false;
      p->jail_turns = 0;
      printf("   Balance: $%d\n", p->balance);
    }
    return false;
  }
}

void handle_build(Player *p) {
  printf("⚙️  Build functionality is not yet implemented.\n");
}

void handle_chance(Player *p) {
  // In a real game, you'd pull from a shuffled deck of indices
  int card_idx = rand() % CHANCE_DECK_SIZE;
  const ChanceCard *card = &CHANCE_DECK[card_idx];

  printf("\n✨ %s\n", card->description);

  switch (card->type) {
  case CHANCE_ADVANCE: {
    int target = card->value;

    // Handle special "Nearest" logic
    if (target == TARGET_NEAREST_UTILITY) {
      while (MONOPOLY_BOARD[p->position].type != TILE_UTILITY) {
        p->position = (p->position + 1) % MONOPOLY_BOARD_SIZE;
      }
    } else if (target == TARGET_NEAREST_RAILROAD) {
      while (MONOPOLY_BOARD[p->position].type != TILE_RAILROAD) {
        p->position = (p->position + 1) % MONOPOLY_BOARD_SIZE;
      }
    } else {
      // Check for passing GO during standard advance
      if (target < p->position) {
        p->balance += 200;
        printf("🎉 Passed GO! Collected $200.\n");
      }
      p->position = target;
    }
    printf("➜ Moved to: %s\n", MONOPOLY_BOARD[p->position].name);
    process_landing(p); // Re-evaluate logic for new square
    break;
  }

  case CHANCE_EARN:
    p->balance += card->value;
    break;

  case CHANCE_SPEND:
    p->balance -= card->value;
    break;

  case CHANCE_BACK:
    p->position =
        (p->position - card->value + MONOPOLY_BOARD_SIZE) % MONOPOLY_BOARD_SIZE;
    printf("⬅️  Moved back to: %s\n", MONOPOLY_BOARD[p->position].name);
    process_landing(p);
    break;

  case CHANCE_JAIL:
    p->position = 10; // Index of Jail
    p->jail_turns = 0;
    p->is_in_jail = true;
    just_sent_to_jail = true;
    printf("🚨 Sent directly to JAIL!\n");
    break;

  case CHANCE_SPEND_EACH_PLAYER:
    for (int i = 0; i < MAX_PLAYERS; i++) {
      if (players[i].id != p->id && !players[i].is_bankrupt) {
        p->balance -= card->value;
        players[i].balance += card->value;
      }
    }
    break;

  case CHANCE_JAIL_FREE:
    p->jail_free_cards++;
    printf("🔑 You received a 'Get Out of Jail Free' card! (Total: %d)\n",
           p->jail_free_cards);
    break;

  case CHANCE_REPAIRS:
    // TODO: Logic placeholder: calculate based on houses/hotels owned
    printf("🔧 Repairs calculated based on your property development.\n");
    break;
  }
}

void handle_community_chest(Player *p) {
  int card_idx = rand() % COMMUNITY_DECK_SIZE;
  const CommunityCard *card = &COMMUNITY_DECK[card_idx];

  printf("\n📦 %s\n", card->description);

  switch (card->type) {
  case COMMUNITY_ADVANCE:
    p->position = card->value;
    p->balance += 200; // Always Go in this set
    printf("➜ Moved to Go! Balance: $%d\n", p->balance);
    break;

  case COMMUNITY_EARN:
    p->balance += card->value;
    break;

  case COMMUNITY_SPEND:
    p->balance -= card->value;
    break;

  case COMMUNITY_EARN_EACH_PLAYER:
    for (int i = 0; i < MAX_PLAYERS; i++) {
      if (players[i].id != p->id && !players[i].is_bankrupt) {
        players[i].balance -= card->value;
        p->balance += card->value;
      }
    }
    break;

  case COMMUNITY_JAIL:
    p->position = 10;
    p->jail_turns = 0;
    p->is_in_jail = true;
    just_sent_to_jail = true;
    printf("🚨 Go to Jail! (Do not pass Go, do not collect $200)\n");
    break;

  case COMMUNITY_REPAIRS:
    // logic for $40/house and $115/hotel
    printf("🔧 Repairs assessed.\n");
    break;

  case COMMUNITY_JAIL_FREE:
    p->jail_free_cards++;
    printf("🔑 Card stored. (Total: %d)\n", p->jail_free_cards);
    break;
  }
}