"""Dev-only tools for testing Paper Dynasty systems. This cog is only loaded when DATABASE != prod. It provides commands for integration testing that create and clean up synthetic test data. """ import logging import os from datetime import date import discord from discord import app_commands from discord.ext import commands from api_calls import db_delete, db_get, db_post from helpers.refractor_constants import TIER_NAMES from helpers.refractor_test_data import ( build_batter_plays, build_decision_data, build_game_data, build_pitcher_plays, calculate_plays_needed, ) CURRENT_SEASON = 11 logger = logging.getLogger(__name__) class CleanupView(discord.ui.View): """Post-test buttons to clean up or keep synthetic game data.""" def __init__( self, owner_id: int, game_id: int, embed: discord.Embed, timeout: float = 300.0 ): super().__init__(timeout=timeout) self.owner_id = owner_id self.game_id = game_id self.embed = embed @discord.ui.button( label="Clean Up Test Data", style=discord.ButtonStyle.danger, emoji="๐Ÿงน" ) async def cleanup_btn( self, interaction: discord.Interaction, button: discord.ui.Button ): if interaction.user.id != self.owner_id: return try: await db_delete("decisions/game", self.game_id) await db_delete("plays/game", self.game_id) await db_delete("games", self.game_id) self.embed.add_field( name="", value=f"๐Ÿงน Test data cleaned up (game #{self.game_id} removed)", inline=False, ) except Exception as e: self.embed.add_field( name="", value=f"โŒ Cleanup failed: {e}", inline=False, ) self.clear_items() await interaction.response.edit_message(embed=self.embed, view=self) self.stop() @discord.ui.button( label="Keep Test Data", style=discord.ButtonStyle.secondary, emoji="๐Ÿ“Œ" ) async def keep_btn( self, interaction: discord.Interaction, button: discord.ui.Button ): if interaction.user.id != self.owner_id: return self.embed.add_field( name="", value=f"๐Ÿ“Œ Test data kept (game #{self.game_id})", inline=False, ) self.clear_items() await interaction.response.edit_message(embed=self.embed, view=self) self.stop() async def on_timeout(self): self.clear_items() class DevToolsCog(commands.Cog): """Dev-only commands for integration testing. Only loaded when DATABASE env var is not 'prod'. """ def __init__(self, bot: commands.Bot): self.bot = bot group_dev = app_commands.Group(name="dev", description="Dev-only testing tools") @group_dev.command( name="refractor-test", description="Run refractor integration test on a card" ) @app_commands.describe(card_id="The batting or pitching card ID to test") async def refractor_test(self, interaction: discord.Interaction, card_id: int): await interaction.response.defer() # --- Phase 1: Setup --- # Look up card (try batting first, then pitching) card = await db_get("battingcards", object_id=card_id) card_type_key = "batting" if card is None: card = await db_get("pitchingcards", object_id=card_id) card_type_key = "pitching" if card is None: await interaction.edit_original_response( content=f"โŒ Card #{card_id} not found (checked batting and pitching)." ) return player_id = card["player"]["id"] player_name = card["player"]["p_name"] team_id = card.get("team_id") or card["player"].get("team_id") if team_id is None: from helpers.main import get_team_by_owner team = await get_team_by_owner(interaction.user.id) if team is None: await interaction.edit_original_response( content="โŒ Could not determine team ID. You must own a team." ) return team_id = team["id"] # Fetch refractor state refractor_data = await db_get( "refractor/cards", params=[("team_id", team_id), ("limit", 100)], ) # Find this player's entry card_state = None if refractor_data and refractor_data.get("items"): for item in refractor_data["items"]: if item["player_id"] == player_id: card_state = item break # Determine current state and thresholds if card_state: current_tier = card_state["current_tier"] current_value = card_state["current_value"] card_type = card_state["track"]["card_type"] next_threshold = card_state["next_threshold"] else: current_tier = 0 current_value = 0 card_type = "batter" if card_type_key == "batting" else "sp" next_threshold = ( 37 if card_type == "batter" else (10 if card_type == "sp" else 3) ) if current_tier >= 4: await interaction.edit_original_response( content=f"โš ๏ธ {player_name} is already at T4 Superfractor โ€” fully evolved." ) return # Calculate plan gap = max(0, next_threshold - current_value) plan = calculate_plays_needed(gap, card_type) # Find an opposing player if card_type == "batter": opposing_cards = await db_get( "pitchingcards", params=[("team_id", team_id), ("variant", 0)], ) else: opposing_cards = await db_get( "battingcards", params=[("team_id", team_id), ("variant", 0)], ) if not opposing_cards or not opposing_cards.get("cards"): await interaction.edit_original_response( content=f"โŒ No opposing {'pitcher' if card_type == 'batter' else 'batter'} cards found on team {team_id}." ) return opposing_player_id = opposing_cards["cards"][0]["player"]["id"] # Build and send initial embed tier_name = TIER_NAMES.get(current_tier, f"T{current_tier}") next_tier_name = TIER_NAMES.get(current_tier + 1, f"T{current_tier + 1}") play_desc = ( f"{plan['num_plays']} HR plays" if card_type == "batter" else f"{plan['num_plays']} K plays" ) embed = discord.Embed( title="Refractor Integration Test", color=0x3498DB, ) embed.add_field( name="Setup", value=( f"**Player:** {player_name} (card #{card_id})\n" f"**Type:** {card_type_key.title()}\n" f"**Current:** T{current_tier} {tier_name} โ†’ **Target:** T{current_tier + 1} {next_tier_name}\n" f"**Value:** {current_value} / {next_threshold} (need {gap} more)\n" f"**Plan:** {play_desc} (+{plan['total_value']:.0f} value)" ), inline=False, ) embed.add_field(name="", value="โณ Executing...", inline=False) await interaction.edit_original_response(embed=embed) # --- Phase 2: Execute --- await self._execute_refractor_test( interaction=interaction, embed=embed, player_id=player_id, player_name=player_name, team_id=team_id, card_type=card_type, card_type_key=card_type_key, opposing_player_id=opposing_player_id, num_plays=plan["num_plays"], ) async def _execute_refractor_test( self, interaction: discord.Interaction, embed: discord.Embed, player_id: int, player_name: str, team_id: int, card_type: str, card_type_key: str, opposing_player_id: int, num_plays: int, ): """Execute phase placeholder โ€” implemented in Task 5.""" raise NotImplementedError("Execute phase not yet implemented") async def setup(bot: commands.Bot): await bot.add_cog(DevToolsCog(bot))