const express = require('express')
const path = require('path')
const { CategoryNewsRepository } = require('../repositories/CategoryNewsRepository')
const { NewsApiClient } = require('../services/NewsApiClient')
const { NewsStorageService } = require('../services/NewsStorageService')
const { PinnedArticleService } = require('../services/PinnedArticleService')
const { NewsRefreshScheduler } = require('../services/NewsRefreshScheduler')
const { NewsController } = require('../controllers/NewsController')
const { NewsApiCompatibilityController } = require('../controllers/NewsApiCompatibilityController')
const { NewsCategoryCatalog } = require('../config/CategoryCatalog')
const { ConsoleLogger } = require('../services/ConsoleLogger')

/**
 * NewsApplication
 *
 * Composes the service graph, configures Express, and manages lifecycle.
 */
class NewsApplication {
  constructor(runtimeConfig) {
    this._runtimeConfig = runtimeConfig
    this._logger = new ConsoleLogger()
    this._categoryCatalog = new NewsCategoryCatalog()
    this._repository = new CategoryNewsRepository(runtimeConfig.dataDirectory)
    this._apiClient = new NewsApiClient({
      pageSize: runtimeConfig.pageSize,
      dataDirectory: runtimeConfig.dataDirectory
    })
    this._pinnedArticleService = new PinnedArticleService(this._apiClient, {
      urls: runtimeConfig.pinnedArticleUrls,
      categoryKeys: runtimeConfig.pinnedCategoryKeys,
      dataDirectory: runtimeConfig.dataDirectory,
      ttlMs: runtimeConfig.pinnedTtlMs
    })
    this._storageService = new NewsStorageService(
      this._categoryCatalog,
      this._repository,
      this._apiClient,
      this._pinnedArticleService
    )
    this._scheduler = new NewsRefreshScheduler(this._storageService, runtimeConfig.refreshCron, this._logger)
    this._express = express()
    this._server = null
  }

  /**
   * @returns {Promise<void>}
   */
  async start() {
    this._configureHttpPipeline()
    await this._scheduler.start()
    await new Promise((resolve) => {
      this._server = this._express.listen(this._runtimeConfig.port, () => {
        this._logger.info('News service started', { port: this._runtimeConfig.port })
        resolve()
      })
    })
  }

  /**
   * @returns {Promise<void>}
   */
  async stop() {
    this._scheduler.stop()
    if (!this._server) {
      return
    }
    await new Promise((resolve, reject) => {
      this._server.close((error) => {
        if (error) {
          reject(error)
          return
        }
        resolve()
      })
    })
  }

  /**
   * @returns {void}
   */
  _configureHttpPipeline() {
    this._express.use(express.json())
    this._express.use('/api/news/images', express.static(path.join(this._runtimeConfig.dataDirectory, 'images')))

    this._express.get('/health', (_request, response) => {
      response.json({
        status: 'ok',
        service: 'news-service',
        timestamp: new Date().toISOString()
      })
    })

    const newsController = new NewsController(this._storageService)
    const newsApiCompatibilityController = new NewsApiCompatibilityController(this._storageService)
    this._express.use('/api/news', newsController.router)
    this._express.use('/v2', newsApiCompatibilityController.router)

    this._express.use((error, _request, response, _next) => {
      this._logger.error('Request processing failed', error)
      response.status(500).json({
        status: 'error',
        message: error.message || 'Internal server error'
      })
    })
  }
}

module.exports = {
  NewsApplication
}
