package lc.esp.eo;

import lc.esp.eo.sdk.EO;
import lc.esp.eo.sdk.EOAssociation;
import lc.esp.eo.sdk.EOId;
import lc.esp.eo.sdk.schema.AssociationSchema;
import lc.esp.eo.sdk.schema.Model;
import lc.esp.eo.sdk.schema.ObjectSchema;
import lc.mecha.log.MechaLogger;
import lc.mecha.log.MechaLoggerFactory;
import lc.mecha.util.VelocityWatch;
import org.junit.jupiter.api.Test;

import java.util.Map;
import java.util.UUID;

import static org.junit.jupiter.api.Assertions.assertEquals;

/**
 * This JUnit test is meant to simulate something approaching a real-world scenario to exercise the system and
 * serve as a sort of example or reference point for the various EO features.
 *
 * @author Alex Leigh
 * @since mk20 (TOKYO PROTOCOL)
 */
public class ScenarioTest {
    private static final MechaLogger logger = MechaLoggerFactory.getLogger(ScenarioTest.class);
    private final static String TYPE_PERSON = "person";
    private final static String PERSON_NAME = "name";
    private final static String KEY_LIKES = "likes";
    private final static String KEY_LIKED_BY = "liked_by";

    private EOId makePerson(EOTransaction txn, String name) {
        return txn.objAdd(TYPE_PERSON, Map.of(PERSON_NAME, name));
    }

    @Test
    public void testScenario() throws Exception {
        int followers = 10;

        // Build our model
        Model model = new Model();
        ObjectSchema person = new ObjectSchema(TYPE_PERSON);
        model.putSchema(person);
        model.putAssociation(new AssociationSchema(KEY_LIKES, KEY_LIKED_BY));
        model.putAssociation(new AssociationSchema(KEY_LIKED_BY, KEY_LIKES));

        try (EOClient client = new EOClient()) {
            try (EOTransaction txn = client.getTransaction("junit_" + UUID.randomUUID(), model)) {
                try {
                    txn.initialize();
                    EOId popularPerson = txn.objAdd(TYPE_PERSON, Map.of(PERSON_NAME, "Alex"));
                    VelocityWatch vw = new VelocityWatch(logger);

                    EOId followerId = null;
                    for (int i = 0; i < followers; i++) {
                        vw.event(TYPE_PERSON);
                        followerId = makePerson(txn, String.valueOf(UUID.randomUUID()));
                        txn.assocAdd(followerId, popularPerson, KEY_LIKES);
                    }
                    vw.status();
                    txn.commit();

                    EO popObj = txn.objGet(popularPerson);
                    logger.info("Popular person: {}", popObj);

                    long cnt = txn.assocCount(popularPerson, KEY_LIKED_BY);
                    logger.info("Person is liked: {}", cnt);
                    assertEquals(followers, cnt);

                    // Should be 2, since we have the inverse.
                    long deleted = txn.assocDelete(popularPerson, followerId, KEY_LIKED_BY);
                    assertEquals(2, deleted);

                    cnt = txn.assocCount(popularPerson, KEY_LIKED_BY);
                    logger.info("Person is liked: {}", cnt);
                    assertEquals(followers - 1, cnt);

                    try (EOAssociationCursor cursor = txn.assocGetTimeRange(popularPerson, KEY_LIKED_BY, 0)) {
                        for (EOAssociation assoc : cursor) {
                            logger.info("Got association: {}", assoc);
                        }
                    }

                    try (EOObjectCursor cursor = txn.objGet(TYPE_PERSON)) {
                        for (EO obj : cursor) {
                            logger.info("Read object: {}", obj);
                        }
                    }
                } finally {
                    txn.drop();
                }
            }
        }
    }
}
