# Complete DOCPLEX implementation - Retry Attempt 1

from docplex.mp.model import Model

def ship_mission_optimization():
    # 1. MODEL & DATA SETUP
    mdl = Model(name="ship_mission_optimization")
    
    # Data from the database
    missions = [
        {'mission_id': 1, 'min_tonnage': 5000, 'min_speed': 20, 'required_nationality': 'USA'},
        {'mission_id': 2, 'min_tonnage': 6000, 'min_speed': 25, 'required_nationality': 'UK'},
        {'mission_id': 3, 'min_tonnage': 7000, 'min_speed': 30, 'required_nationality': 'Canada'}
    ]
    
    ships = [
        {'ship_id': 1, 'tonnage': 5500, 'speed_knots': 22, 'nationality': 'USA'},
        {'ship_id': 2, 'tonnage': 6500, 'speed_knots': 27, 'nationality': 'UK'},
        {'ship_id': 3, 'tonnage': 7500, 'speed_knots': 32, 'nationality': 'Canada'}
    ]
    
    # Cost coefficients
    c_tonnage = 1
    c_speed = 1
    
    # 2. VARIABLES
    x = {(i['ship_id'], j['mission_id']): mdl.binary_var(name=f"x_{i['ship_id']}_{j['mission_id']}") 
         for i in ships for j in missions}
    
    # 3. OBJECTIVE FUNCTION
    objective = mdl.sum((c_tonnage * i['tonnage'] + c_speed * i['speed_knots']) * x[(i['ship_id'], j['mission_id'])] 
                        for i in ships for j in missions)
    mdl.minimize(objective)
    
    # 4. CONSTRAINTS
    
    # Mission Assignment Constraint: Each mission must be assigned to exactly one ship
    for j in missions:
        mdl.add_constraint(mdl.sum(x[(i['ship_id'], j['mission_id'])] for i in ships) == 1, 
                           ctname=f"mission_assignment_{j['mission_id']}")
    
    # Ship Assignment Constraint: Each ship can be assigned to at most one mission
    for i in ships:
        mdl.add_constraint(mdl.sum(x[(i['ship_id'], j['mission_id'])] for j in missions) <= 1, 
                           ctname=f"ship_assignment_{i['ship_id']}")
    
    # Tonnage Requirement Constraint: The tonnage of the ship must meet or exceed the mission's minimum tonnage
    for i in ships:
        for j in missions:
            mdl.add_constraint(i['tonnage'] * x[(i['ship_id'], j['mission_id'])] >= j['min_tonnage'] * x[(i['ship_id'], j['mission_id'])], 
                               ctname=f"tonnage_requirement_{i['ship_id']}_{j['mission_id']}")
    
    # Speed Requirement Constraint: The speed of the ship must meet or exceed the mission's minimum speed
    for i in ships:
        for j in missions:
            mdl.add_constraint(i['speed_knots'] * x[(i['ship_id'], j['mission_id'])] >= j['min_speed'] * x[(i['ship_id'], j['mission_id'])], 
                               ctname=f"speed_requirement_{i['ship_id']}_{j['mission_id']}")
    
    # Nationality Requirement Constraint: The nationality of the ship must match the mission's required nationality
    for i in ships:
        for j in missions:
            mdl.add_constraint((i['nationality'] == j['required_nationality']) * x[(i['ship_id'], j['mission_id'])] == x[(i['ship_id'], j['mission_id'])], 
                               ctname=f"nationality_requirement_{i['ship_id']}_{j['mission_id']}")
    
    # 5. SOLVING & RESULTS
    solution = mdl.solve()
    
    if solution:
        print(f"Optimal value: {solution.objective_value}")
        for i in ships:
            for j in missions:
                if solution.get_value(x[(i['ship_id'], j['mission_id'])]) > 0.5:
                    print(f"Ship {i['ship_id']} assigned to Mission {j['mission_id']}")
    else:
        print("No solution found")
        print(f"Status: {mdl.solve_details.status}")
    
    return mdl

# Execute the optimization
ship_mission_optimization()