require "crypto/bcrypt/password"
require "jennifer/model/authentication"

module ContainerMom::Models
  class User < Jennifer::Model::Base
    include Jennifer::Model::Authentication

    with_timestamps

    mapping(
      id: Primary64,
      email: String,
      password_hash: String,
      first_name: String?,
      last_name: String?,
      company_name: String?,
      stripe_customer_id: String?,
      role: String,
      active: Bool,
      email_verified: Bool,
      created_at: Time?,
      updated_at: Time?
    )

    has_many :sessions, Session, dependent: :destroy
    has_many :api_tokens, ApiToken, dependent: :destroy

    validates_presence :email
    validates_uniqueness :email
    validates_format :email, /\A[\w+\-.]+@[a-z\d\-]+(\.[a-z\d\-]+)*\.[a-z]+\z/i
    validates_presence :password_hash
    validates_inclusion :role, ["admin", "user"]

    def self.authenticate(email : String, password : String) : User?
      user = self.find_by(email: email, active: true)
      return nil unless user

      if Crypto::Bcrypt::Password.new(user.password_hash).verify(password)
        user
      else
        nil
      end
    end

    def self.create_with_password(attributes, password : String) : User
      password_hash = Crypto::Bcrypt::Password.create(password).to_s
      create(attributes.merge({password_hash: password_hash}))
    end

    def name : String
      if first_name? || last_name?
        [first_name?, last_name?].compact.join(" ")
      else
        email
      end
    end

    def admin? : Bool
      role == "admin"
    end

    def verify_email! : Bool
      update(email_verified: true)
    end

    def create_session(user_agent : String?, ip_address : String?) : Session
      Session.create({
        user_id: id,
        token: Random::Secure.hex(32),
        user_agent: user_agent,
        ip_address: ip_address,
        expires_at: 30.days.from_now
      })
    end

    def create_api_token(name : String, description : String? = nil) : ApiToken
      token = Random::Secure.hex(32)
      token_hash = Crypto::Bcrypt::Password.create(token).to_s

      api_token = ApiToken.create({
        user_id: id,
        name: name,
        token_hash: token_hash,
        description: description,
        active: true
      })

      # Return both the token and the model
      {token, api_token}
    end
  end
end
