/*
 * Jooby https://jooby.io
 * Apache License Version 2.0 https://jooby.io/LICENSE.txt
 * Copyright 2014 Edgar Espina
 */
package io.jooby.internal;

import java.time.Instant;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import edu.umd.cs.findbugs.annotations.NonNull;
import edu.umd.cs.findbugs.annotations.Nullable;
import io.jooby.Context;
import io.jooby.Session;
import io.jooby.SessionStore;
import io.jooby.value.Value;

public class SessionImpl implements Session {

  private Context ctx;

  private boolean isNew;

  private String id;

  private Map<String, String> attributes;

  private Instant creationTime;

  private Instant lastAccessedTime;

  private boolean modify;

  public SessionImpl(Context ctx, String id) {
    this(ctx, id, new ConcurrentHashMap<>());
  }

  public SessionImpl(Context ctx, String id, Map<String, String> attributes) {
    this.ctx = ctx;
    this.id = id;
    this.attributes = attributes;
  }

  @Override
  public boolean isNew() {
    return isNew;
  }

  @NonNull @Override
  public Session setNew(boolean aNew) {
    this.isNew = aNew;
    return this;
  }

  @Override
  public boolean isModify() {
    return modify;
  }

  @NonNull @Override
  public Session setModify(boolean modify) {
    this.modify = modify;
    return this;
  }

  @Override
  public @Nullable String getId() {
    return id;
  }

  @NonNull @Override
  public Session setId(@Nullable String id) {
    this.id = id;
    return this;
  }

  @Override
  public @NonNull Value get(@NonNull String name) {
    return Value.create(ctx.getValueFactory(), name, attributes.get(name));
  }

  @Override
  public @NonNull Session put(@NonNull String name, @NonNull String value) {
    attributes.put(name, value);
    updateState();
    return this;
  }

  public @NonNull Session put(@NonNull String name, Object value) {
    attributes.put(name, value.toString());
    return this;
  }

  @Override
  public @NonNull Value remove(@NonNull String name) {
    var value = get(name);
    attributes.remove(name);
    updateState();
    return value;
  }

  @Override
  public @NonNull Map<String, String> toMap() {
    return attributes;
  }

  @Override
  public @NonNull Instant getCreationTime() {
    return creationTime;
  }

  @NonNull @Override
  public Session setCreationTime(@NonNull Instant creationTime) {
    this.creationTime = creationTime;
    return this;
  }

  @Override
  public @NonNull Instant getLastAccessedTime() {
    return lastAccessedTime;
  }

  @Override
  public @NonNull Session setLastAccessedTime(@NonNull Instant lastAccessedTime) {
    this.lastAccessedTime = lastAccessedTime;
    return this;
  }

  @NonNull @Override
  public Session clear() {
    attributes.clear();
    updateState();
    return this;
  }

  @Override
  public void destroy() {
    ctx.getAttributes().remove(NAME);
    attributes.clear();
    store(ctx).deleteSession(ctx, this);
  }

  @NonNull @Override
  public Session renewId() {
    store(ctx).renewSessionId(ctx, this);
    updateState();
    return this;
  }

  private void updateState() {
    modify = true;
    lastAccessedTime = Instant.now();
    store(ctx).touchSession(ctx, this);
  }

  private static SessionStore store(Context ctx) {
    return ctx.getRouter().getSessionStore();
  }
}
