import autoprefixer from 'autoprefixer'
import concat from 'gulp-concat'
import csso from 'gulp-csso'
import { deleteAsync } from 'del'
import gulp from 'gulp'
import gulpWebp from 'gulp-webp'
import imagemin, { mozjpeg, optipng, svgo } from 'gulp-imagemin'
import imageminAvif from 'imagemin-avif'
import order from 'gulp-order'
import plumber from 'gulp-plumber'
import postcss from 'gulp-postcss'
import pug from 'gulp-pug'
import rename from 'gulp-rename'
import uglify from 'gulp-uglify-es'
import * as dartSass from 'sass'
import gulpSass from 'gulp-sass'
import sourcemap from 'gulp-sourcemaps'
import svgstore from 'gulp-svgstore'
import sync from 'browser-sync'
import { nanoid } from 'nanoid'
import postcssUrl from 'postcss-url'
import inject from 'gulp-inject'
import newer from 'gulp-newer' // Added gulp-newer

const baseUrl = '.'
const pathCss = '..'
const searchId = Date.now()

const getImageSrcset = (path, format, params) => {
  return [1, 2]
    .map((dpr) => `${baseUrl}${path}@${dpr}x.${format}${params} ${dpr}x`)
    .join(', ')
}

const getImageMime = (format) => {
  switch (format) {
    case 'avif':
      return 'image/avif'
    case 'webp':
      return 'image/webp'
    case 'jpeg':
    case 'jpg':
      return 'image/jpeg'
    case 'png':
      return 'image/png'
    default:
      return null
  }
}

const sass = gulpSass(dartSass)

const clean = async () => {
  return await deleteAsync(['dist'])
}

const copy = () => {
  return gulp
    .src(
      [
        'src/fonts/**/*.{woff,woff2}',
        'src/img/**/*.{webm,webp,avif,jpg,jpeg,png,svg}',
        'src/favicon/**/*',
        'src/favicon.ico',
        'src/settings.js',
        'src/urls.json',
        'src/robots.txt'
      ],
      { base: 'src', encoding: false }
    )
    .pipe(gulp.dest('dist'))
}

const css = () => {
  return gulp
    .src('src/scss/index.scss')
    .pipe(plumber())
    .pipe(sourcemap.init())
    .pipe(sass.sync().on('error', sass.logError))
    .pipe(
      postcss([
        postcssUrl({
          url: (asset) => {
            if (asset.url.startsWith('/')) {
              return `${pathCss}${asset.url}?v=${searchId}`
            }
            return asset.url
          }
        }),
        autoprefixer({ remove: false })
      ])
    )
    .pipe(rename('style.css'))
    .pipe(sourcemap.write('.'))
    .pipe(gulp.dest('dist/css'))
    .pipe(sync.stream())
    .pipe(csso())
    .pipe(rename('style.min.css'))
    .pipe(gulp.dest('dist/css'))
}

const images = () => {
  return gulp
    .src('src/img/**/*.{png,jpg,jpeg,svg}', { base: 'src', encoding: false }) // Process all images in subfolders, keep base for dest path
    .pipe(newer('dist')) // Process only newer files, compare with base 'dist' (e.g. dist/img/...)
    .pipe(
      imagemin(
        [
          optipng({ optimizationLevel: 3 }),
          mozjpeg({ quality: 80, progressive: true }), // Quality for JPEG
          svgo({
            plugins: [{ name: 'removeUnknownsAndDefaults', active: false }]
          })
        ],
        { silent: true }
      )
    )
    .pipe(gulp.dest('dist')) // Output to dist, maintaining folder structure from base
}

const webp = () => {
  return gulp
    .src('src/img/**/*.{png,jpg,jpeg}', { base: 'src', encoding: false }) // Process all images in subfolders
    .pipe(newer({ dest: 'dist', ext: '.webp' })) // Process only newer, check against .webp extension in dist
    .pipe(gulpWebp({ quality: 80 })) // Quality for WebP
    .pipe(gulp.dest('dist')) // Output to dist, maintaining folder structure
}

const avif = () => {
  return gulp
    .src('src/img/**/*.{png,jpg,jpeg}', { base: 'src', encoding: false }) // Process all images in subfolders
    .pipe(newer({ dest: 'dist', ext: '.avif' })) // Process only newer, check against .avif extension in dist
    .pipe(imagemin([imageminAvif({ quality: 50 })], { silent: true })) // Quality for AVIF
    .pipe(rename((path) => (path.extname = '.avif')))
    .pipe(gulp.dest('dist')) // Output to dist, maintaining folder structure
}

const processImages = gulp.parallel(images, webp, avif)

const sprite = () => {
  return gulp
    .src('src/icons/**/*.svg')
    .pipe(svgstore({ inlineSvg: true }))
    .pipe(rename(`sprite.svg`))
    .pipe(gulp.dest('dist/img'))
}

const injectSprite = () => {
  const spritePath = 'dist/img/sprite.svg'

  return gulp
    .src('dist/*.html')
    .pipe(
      inject(gulp.src(spritePath), {
        transform: (_, file) => {
          return file.contents.toString()
        }
      })
    )
    .pipe(gulp.dest('dist'))
}

const jsCommon = () => {
  return gulp
    .src('src/js/common/*.js')
    .pipe(
      plumber({
        errorHandler(err) {
          console.error(err.toString())
          this.emit('end')
        }
      })
    )
    .pipe(order(['_utils.js', '*.js']))
    .pipe(concat(`script.js`))
    .pipe(gulp.dest('dist/js'))
    .pipe(uglify.default())
    .pipe(rename(`script.min.js`))
    .pipe(gulp.dest('dist/js'))
}

const jsVendor = () => {
  return gulp
    .src('src/js/vendor/*.js')
    .pipe(
      plumber({
        errorHandler(err) {
          console.error(err.toString())
          this.emit('end')
        }
      })
    )
    .pipe(concat(`vendor.js`))
    .pipe(gulp.dest('dist/js'))
    .pipe(uglify.default())
    .pipe(rename(`script.min.js`))
    .pipe(gulp.dest('dist/js'))
}

const html = () => {
  return gulp
    .src('src/pug/pages/**/*.pug')
    .pipe(plumber())
    .pipe(
      pug({
        pretty: true,
        basedir: 'src/pug',
        locals: {
          baseUrl,
          searchId,
          getSrcset: getImageSrcset,
          getMime: getImageMime
        }
      })
    )
    .pipe(gulp.dest('dist'))
}

const refresh = (done) => {
  sync.reload()
  done()
}

const server = () => {
  sync.init({
    server: 'dist/',
    notify: false,
    open: false,
    cors: true,
    ui: false
  })

  gulp.watch('src/pug/**/*.{pug,js}', gulp.series(html, injectSprite, refresh))
  gulp.watch(
    'src/icons/**/*.svg',
    gulp.series(sprite, html, injectSprite, refresh)
  )
  gulp.watch('src/scss/**/*.scss', gulp.series(css))
  gulp.watch('src/js/**/*.js', gulp.series(jsVendor, jsCommon, refresh))
  // Watch for source image changes and run all image processing tasks.
  // gulp-newer within each task will ensure only necessary files are processed.
  gulp.watch('src/img/**/*.{png,jpg,jpeg,svg}', gulp.series(processImages, refresh))
}

export const build = gulp.series(
  clean,
  copy,
  css,
  jsVendor,
  jsCommon,
  processImages,
  sprite,
  html,
  injectSprite
)

export const dev = gulp.series(build, server)
