import type { Session, SessionStorage, CookieOptions } from '../types'

export class CookieSessionStorage implements SessionStorage {
  private options: CookieOptions

  constructor(options: CookieOptions) {
    this.options = options
  }

  async get(key: string): Promise<string | null> {
    if (typeof document === 'undefined') {
      return null
    }

    const cookies = document.cookie.split(';')
    for (const cookie of cookies) {
      const [name, value] = cookie.trim().split('=')
      if (name === key) {
        return decodeURIComponent(value)
      }
    }
    return null
  }

  async set(key: string, value: string): Promise<void> {
    if (typeof document === 'undefined') {
      return
    }

    const encodedValue = encodeURIComponent(value)
    const expires = this.options.maxAge ? `max-age=${this.options.maxAge}` : ''
    const secure = this.options.secure ? 'secure' : ''
    const sameSite = `samesite=${this.options.sameSite}`
    const path = `path=${this.options.path}`
    const domain = this.options.domain ? `domain=${this.options.domain}` : ''

    const cookieString = [
      `${key}=${encodedValue}`,
      expires,
      secure,
      sameSite,
      path,
      domain,
    ].filter(Boolean).join('; ')

    document.cookie = cookieString
  }

  async remove(key: string): Promise<void> {
    if (typeof document === 'undefined') {
      return
    }

    const cookieString = `${key}=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=${this.options.path}`
    document.cookie = cookieString
  }

  async clear(): Promise<void> {
    if (typeof document === 'undefined') {
      return
    }

    const cookies = document.cookie.split(';')
    for (const cookie of cookies) {
      const [name] = cookie.trim().split('=')
      await this.remove(name)
    }
  }
}

export class LocalSessionStorage implements SessionStorage {
  async get(key: string): Promise<string | null> {
    if (typeof localStorage === 'undefined') {
      return null
    }
    return localStorage.getItem(key)
  }

  async set(key: string, value: string): Promise<void> {
    if (typeof localStorage === 'undefined') {
      return
    }
    localStorage.setItem(key, value)
  }

  async remove(key: string): Promise<void> {
    if (typeof localStorage === 'undefined') {
      return
    }
    localStorage.removeItem(key)
  }

  async clear(): Promise<void> {
    if (typeof localStorage === 'undefined') {
      return
    }
    localStorage.clear()
  }
}

export class SessionManager {
  private storage: SessionStorage
  private sessionKey: string

  constructor(storage: SessionStorage, sessionKey: string = 'auth_session') {
    this.storage = storage
    this.sessionKey = sessionKey
  }

  async getSession(): Promise<Session | null> {
    try {
      const sessionData = await this.storage.get(this.sessionKey)
      if (!sessionData) {
        return null
      }

      const session: Session = JSON.parse(sessionData)
      
      if (this.isSessionExpired(session)) {
        await this.clearSession()
        return null
      }

      return session
    } catch (error) {
      await this.clearSession()
      return null
    }
  }

  async setSession(session: Session): Promise<void> {
    try {
      const sessionData = JSON.stringify(session)
      await this.storage.set(this.sessionKey, sessionData)
    } catch (error) {
      throw new Error('Failed to save session')
    }
  }

  async clearSession(): Promise<void> {
    try {
      await this.storage.remove(this.sessionKey)
    } catch (error) {
      throw new Error('Failed to clear session')
    }
  }

  async refreshSession(refreshToken: string, refreshFn: (token: string) => Promise<Session | null>): Promise<Session | null> {
    try {
      const newSession = await refreshFn(refreshToken)
      if (newSession) {
        await this.setSession(newSession)
      }
      return newSession
    } catch (error) {
      await this.clearSession()
      return null
    }
  }

  private isSessionExpired(session: Session): boolean {
    const now = Date.now()
    const expiresAt = session.expiresAt * 1000
    return now >= expiresAt
  }

  isTokenExpiringSoon(session: Session, thresholdMinutes: number = 5): boolean {
    const now = Date.now()
    const expiresAt = session.expiresAt * 1000
    const threshold = thresholdMinutes * 60 * 1000
    return (expiresAt - now) <= threshold
  }
}

export function createSessionManager(storage: SessionStorage, sessionKey?: string): SessionManager {
  return new SessionManager(storage, sessionKey)
}

export function createCookieStorage(options: CookieOptions): CookieSessionStorage {
  return new CookieSessionStorage(options)
}

export function createLocalStorage(): LocalSessionStorage {
  return new LocalSessionStorage()
}

export function isServerSide(): boolean {
  return typeof window === 'undefined'
}

export function validateSession(session: Session): boolean {
  if (!session || !session.user || !session.accessToken) {
    return false
  }

  if (session.expiresAt && session.expiresAt * 1000 <= Date.now()) {
    return false
  }

  return true
}