import { DataLoader } from '../utils/dataLoader.js';
import { ToolResponse } from '../types/stateData.js';
import {
validateStateList,
validateComparisonAspect,
handleToolError,
logger
} from '../utils/errorHandler.js';
export const compareJurisdictionsTool = {
name: "compare_jurisdictions",
description: "Compare gambling regulations between multiple states",
inputSchema: {
type: "object",
properties: {
states: {
type: "array",
items: { type: "string" },
minItems: 2,
description: "States to compare (minimum 2)"
},
aspect: {
type: "string",
enum: ["licensing", "fees", "compliance", "taxes", "all"],
description: "Regulatory aspect to compare"
}
},
required: ["states", "aspect"]
}
};
function extractTaxRate(taxation: any): number | undefined {
// Try multiple field name variations
return taxation?.sports_betting?.rate ||
taxation?.mobile_sports_betting?.rate ||
taxation?.casino_gaming_tax?.rates?.[taxation.casino_gaming_tax.rates.length - 1]?.rate;
}
export async function handleCompareJurisdictions(
args: { states: string[]; aspect: string },
dataLoader: DataLoader
): Promise<ToolResponse> {
try {
logger.info('compare_jurisdictions called', args);
validateStateList(args.states);
validateComparisonAspect(args.aspect);
if (args.states.length < 2) {
throw new Error('At least 2 states are required for comparison');
}
const statesData = await dataLoader.loadMultipleStates(args.states);
const comparison: any = {
states: statesData.map(s => s.state_name),
comparison_date: new Date().toISOString().split('T')[0],
aspect: args.aspect,
data: {}
};
if (args.aspect === 'taxes' || args.aspect === 'all') {
comparison.data.tax_comparison = statesData.map(state => {
const taxation = state.taxation as any;
const sportsBettingRate = extractTaxRate(taxation);
return {
state: state.state_name,
sports_betting_rate: sportsBettingRate,
mobile_rate: taxation?.mobile_sports_betting?.rate,
retail_rate: taxation?.retail_sports_betting?.rate,
igaming_slots_rate: taxation?.interactive_gaming?.online_slots?.rate ||
taxation?.interactive_gaming_proposed?.rate,
igaming_table_rate: taxation?.interactive_gaming?.online_table_games?.rate ||
taxation?.commercial_casino_existing?.table_games?.rate,
structure: taxation ? 'available' : 'not available',
notes: taxation?.mobile_sports_betting?.note || taxation?.sports_betting?.note
};
}).sort((a, b) => (a.sports_betting_rate || 999) - (b.sports_betting_rate || 999));
}
if (args.aspect === 'licensing' || args.aspect === 'all') {
comparison.data.licensing_comparison = statesData.map(state => ({
state: state.state_name,
regulator: state.regulator,
license_types_available: state.license_types ? Object.keys(state.license_types).length : 0,
has_detailed_info: state.license_types && Object.keys(state.license_types).length > 0
}));
}
if (args.aspect === 'compliance' || args.aspect === 'all') {
comparison.data.compliance_comparison = statesData.map(state => ({
state: state.state_name,
has_compliance_data: !!state.compliance_requirements,
regulator: state.regulator
}));
}
comparison.summary = {
total_states: statesData.length,
states_with_complete_data: statesData.filter(s =>
s.license_types && Object.keys(s.license_types).length > 0
).length,
states_with_limited_data: statesData.filter(s =>
!s.license_types || Object.keys(s.license_types).length === 0
).map(s => s.state_name)
};
return {
content: [{
type: "text",
text: JSON.stringify(comparison, null, 2)
}]
};
} catch (error) {
return handleToolError(error);
}
}