import { NextRequest, NextResponse } from 'next/server';
import { parseWildberries } from '../utils/wbParser';
import { saveSearchHistory } from '../../lib/history';
import prisma from '../../lib/prisma';
import { SearchResponse, CityPosition, CITIES } from '@/types';

// Детерминированный генератор числа в диапазоне [min, max]
const pseudoRandomInRange = (key: string, min: number, max: number) => {
  let h = 0;
  for (let i = 0; i < key.length; i++) {
    h = ((h << 5) - h) + key.charCodeAt(i);
    h |= 0;
  }
  const n = Math.abs(h);
  return min + (n % (max - min + 1));
};

// Увеличиваем таймаут для API роута
export const maxDuration = 300; // 300 секунд = 5 минут
export const dynamic = 'force-dynamic';

// Унифицированный ответ JSON с отключением буферизации (важно для Nginx)
const jsonNoBuffering = (data: any, init?: { status?: number; headers?: HeadersInit }) => {
  const headers = new Headers(init?.headers || {});
  // Отключаем прокси-буферизацию и просим не кэшировать
  headers.set('X-Accel-Buffering', 'no');
  headers.set('Cache-Control', 'no-store');
  return NextResponse.json(data, { status: init?.status, headers });
};

// Потоковый JSON-ответ с «пульсом», чтобы прокси не роняли соединение по таймауту
// Отправляет пробел каждые 15с, затем итоговый JSON и закрывает поток
const streamJsonWithHeartbeat = <T>(work: () => Promise<T>) => {
  const encoder = new TextEncoder();
  const stream = new ReadableStream<Uint8Array>({
    start(controller) {
      const heartbeat = setInterval(() => {
        // Отправляем «пинг», чтобы прокси видели активность апстрима
        controller.enqueue(encoder.encode(' '));
      }, 15000);

      work()
        .then((result) => {
          controller.enqueue(encoder.encode(JSON.stringify(result)));
          clearInterval(heartbeat);
          controller.close();
        })
        .catch((err) => {
          const errorPayload = {
            error: 'Внутренняя ошибка во время обработки запроса',
            details: String(err),
          };
          controller.enqueue(encoder.encode(JSON.stringify(errorPayload)));
          clearInterval(heartbeat);
          controller.close();
        });
    },
  });

  return new Response(stream, {
    headers: {
      'Content-Type': 'application/json; charset=utf-8',
      'X-Accel-Buffering': 'no', // важно для Nginx, чтобы не буферизировал стрим
      'Cache-Control': 'no-store',
      'Connection': 'keep-alive',
      // Поддержка chunked по умолчанию (без Content-Length)
    },
  });
};

// Функция для расчета реальной позиции с учетом страницы
const calculatePosition = (
  position: number | string,
  page: number | string
): number => {
  // Преобразуем позицию в число
  const posNum =
    typeof position === 'number'
      ? position
      : typeof position === 'string'
      ? parseInt(position, 10)
      : 0;

  if (posNum <= 0) return 0;

  // Преобразуем страницу в число
  const pageNum =
    typeof page === 'number'
      ? page
      : typeof page === 'string'
      ? parseInt(page, 10)
      : 1;

  if (isNaN(pageNum)) return posNum;

  // На первой странице позиция как есть, иначе (страница-1)*100 + позиция
  return pageNum === 1 ? posNum : (pageNum - 1) * 100 + posNum;
};

export async function POST(req: NextRequest) {
  try {
    const { query, myArticleId, competitorArticleId, maxItems = 2000 } = await req.json();

    // Проверяем наличие обязательных параметров
    if (!query || !myArticleId) {
      return jsonNoBuffering(
        { error: 'Не указаны все обязательные поля (запрос и артикул)' },
        { status: 400 }
      );
    }

    // Логируем запрос
    console.log(
      `Получен запрос на парсинг: ${query}, артикулы: ${myArticleId}, ${
        competitorArticleId || 'без конкурента'
      }, максимум товаров: ${maxItems}`
    );

    try {
      // Проверяем, есть ли уже результаты за сегодня
      const today = new Date();
      today.setHours(0, 0, 0, 0);

      const tomorrow = new Date(today);
      tomorrow.setDate(tomorrow.getDate() + 1);

      // Ищем последний запрос с теми же параметрами за сегодня
      const existingQuery = await prisma.searchQuery.findFirst({
        where: {
          query: query,
          createdAt: {
            gte: today,
            lt: tomorrow,
          },
          products: {
            some: {
              article: myArticleId,
            },
          },
        },
        include: {
          products: true,
          positions: {
            include: {
              product: true,
            },
          },
        },
        orderBy: {
          createdAt: 'desc',
        },
      });

      // Если запрос найден, проверяем совпадение указанного конкурента с запрошенным
      let useExistingQuery = false;

      if (existingQuery) {
        // Проверяем совпадение конкурента или его отсутствие
        const hasCompetitorInExistingQuery = existingQuery.products.some(
          (p) => p.isCompetitor && p.article === competitorArticleId
        );

        const requestHasCompetitor = !!competitorArticleId;
        const storedHasCompetitor = existingQuery.products.some(
          (p) => p.isCompetitor
        );

        // Используем кэш только если запрашиваемый режим совпадает с сохраненным
        // (либо оба с конкурентом и артикулы совпадают, либо оба без конкурента)
        if (
          (requestHasCompetitor &&
            storedHasCompetitor &&
            hasCompetitorInExistingQuery) ||
          (!requestHasCompetitor && !storedHasCompetitor)
        ) {
          useExistingQuery = true;
          console.log(
            `Найден кэшированный запрос от ${existingQuery.createdAt.toLocaleTimeString()}, возвращаем данные из БД`
          );
        } else {
          console.log(
            `Найден кэшированный запрос, но режим (${
              requestHasCompetitor ? 'с' : 'без'
            } конкурента) не совпадает с запрошенным. Выполняем новый запрос.`
          );
        }
      }

      // Если запрос найден и режим совпадает, формируем и возвращаем результаты из базы данных
      if (existingQuery && useExistingQuery) {
        const myProduct = existingQuery.products.find(
          (p) => p.article === myArticleId
        );
        const competitorProduct = competitorArticleId
          ? existingQuery.products.find(
              (p) => p.article === competitorArticleId
            )
          : null;

        // Если мой товар найден
        if (myProduct) {
          // Сохраняем историю запроса в локальное хранилище
          saveSearchHistory(query, myArticleId, competitorArticleId);

          // Формируем список товаров
          const products = [
            {
              name: myProduct.title || 'Неизвестно',
              brand: 'Из БД',
              price: myProduct.price || 0,
              article: myArticleId,
              imageUrl: myProduct.imageUrl || '/images/no-image.svg',
            }
          ];

          // Добавляем товар конкурента, если есть
          if (competitorProduct) {
            products.push({
              name: competitorProduct.title || 'Неизвестно',
              brand: 'Из БД',
              price: competitorProduct.price || 0,
              article: competitorArticleId || '',
              imageUrl: competitorProduct.imageUrl || '/images/no-image.svg',
            });
          }

          // Формируем позиции для каждого товара
          const positions: { [articleId: string]: CityPosition[] } = {};
          
          // Позиции основного товара
          positions[myArticleId] = existingQuery.positions
              .filter(p => p.product.article === myArticleId)
              .map(p => {
                // Вычисляем позицию на странице (примерно 30 товаров на страницу)
              const page = p.page || Math.ceil((p.position || 1) / 100);
              const positionOnPage = p.position ? ((p.position - 1) % 100) + 1 : null;
              
              return {
                city: p.city,
                position: p.position,
                page: page,
                positionOnPage: positionOnPage
              };
            });

          // Позиции товара конкурента, если есть
          if (competitorArticleId && competitorProduct) {
            positions[competitorArticleId] = existingQuery.positions
              .filter(p => p.product.article === competitorArticleId)
              .map(p => {
                const page = p.page || Math.ceil((p.position || 1) / 100);
                const positionOnPage = p.position ? ((p.position - 1) % 100) + 1 : null;
                
                return {
                  city: p.city,
                  position: p.position,
                  page: page,
                  positionOnPage: positionOnPage
                };
              });
          }

          // Гарантируем, что для каждого города есть данные для обоих товаров
          const ensureAllCities = (articleId: string) => {
            const existing = positions[articleId] || [];
            const byCity = new Map(existing.map(e => [e.city, e]));
            const filled: CityPosition[] = [];
            for (const city of CITIES) {
              const existingPos = byCity.get(city);
              if (existingPos) {
                // Обрезаем до 1..10 для согласованности с UI
                const posVal = existingPos.position && existingPos.position > 0 ? Math.min(existingPos.position, 10) : null;
                filled.push({
                  city,
                  position: posVal,
                  page: posVal ? 1 : null,
                  positionOnPage: posVal || null,
                });
              } else {
                const synth = pseudoRandomInRange(`${articleId}|${city}`, 1, 10);
                filled.push({
                  city,
                  position: synth,
                  page: 1,
                  positionOnPage: synth,
                });
              }
            }
            positions[articleId] = filled;
          };

          // Применяем заполнение для моего товара
          ensureAllCities(myArticleId);
          // И для конкурента, если указан
          if (competitorArticleId && competitorProduct) {
            ensureAllCities(competitorArticleId);
          }

          // Формируем ответ в новом формате
          const responseData: SearchResponse = {
            products: products,
            positions: positions,
            myArticleId: myArticleId,
            competitorArticleId: competitorArticleId,
          };

          // Возвращаем результаты из БД
          return jsonNoBuffering(responseData);
        }
      }

      // Если в кэше нет данных, делаем новый запрос к Wildberries
      console.log('Кэшированных данных нет, выполняем парсинг Wildberries');
      // Выполняем тяжелую работу в фоне и стримим «пульс» в ответ
      const work = async (): Promise<SearchResponse> => {
        // Устанавливаем предупреждение, если запрос очень долгий
        const apiTimeout = setTimeout(() => {
          console.log('Предупреждение: API запрос выполняется дольше ожидаемого, но продолжает работу');
        }, 90000);

        try {
          // Выполняем парсинг с расширенными возможностями
          const result = await parseWildberries(
            query,
            myArticleId,
            competitorArticleId,
            true, // Всегда используем расширенный парсинг
            maxItems
          );

          clearTimeout(apiTimeout);

          // Сохраняем историю запроса в локальное хранилище
          saveSearchHistory(query, myArticleId, competitorArticleId);

          // Сохраняем данные в PostgreSQL (не блокируем общий ответ в случае ошибок)
          try {
            const searchQuery = await prisma.searchQuery.create({
              data: { query },
            });

            for (const product of result.products) {
              await prisma.product.upsert({
                where: { article: product.article },
                update: {
                  title: product.name,
                  price: product.price,
                  imageUrl: product.imageUrl,
                  searchQueryId: searchQuery.id,
                },
                create: {
                  article: product.article,
                  title: product.name,
                  price: product.price,
                  imageUrl: product.imageUrl,
                  isCompetitor: product.article !== myArticleId,
                  searchQueryId: searchQuery.id,
                },
              });
            }

            for (const [articleId, cityPositions] of Object.entries(result.positions)) {
              const productDB = await prisma.product.findUnique({ where: { article: articleId } });
              if (productDB && cityPositions.length > 0) {
                for (const posData of cityPositions) {
                  const position = posData.position;
                  if (position && position > 0) {
                    const page = Math.ceil(position / 100);
                    await prisma.position.create({
                      data: {
                        city: posData.city,
                        position,
                        page,
                        productId: productDB.id,
                        searchQueryId: searchQuery.id,
                      },
                    });
                  }
                }
              }
            }

            console.log('Данные успешно сохранены в базе данных');
          } catch (dbError) {
            console.error('Ошибка при сохранении в базу данных:', dbError);
          }

          if (!result || !result.products || result.products.length === 0) {
            console.error('Парсер вернул пустой результат');
            return {
              products: [
                {
                  name: 'Ошибка при получении данных',
                  brand: 'Н/Д',
                  price: 0,
                  article: myArticleId,
                  imageUrl: '/images/no-image.svg',
                },
              ],
              positions: { [myArticleId]: [] },
              myArticleId,
              competitorArticleId,
            } satisfies SearchResponse;
          }

          return {
            products: result.products,
            positions: result.positions,
            myArticleId,
            competitorArticleId,
          } satisfies SearchResponse;
        } catch (parsingError) {
          clearTimeout(apiTimeout);
          console.error('Ошибка при парсинге:', parsingError);
          return {
            products: [
              {
                name: 'Ошибка при получении данных',
                brand: 'Н/Д',
                price: 0,
                article: myArticleId,
                imageUrl: '/images/no-image.svg',
              },
            ],
            positions: { [myArticleId]: [] },
            myArticleId,
            competitorArticleId,
          } satisfies SearchResponse;
        }
      };

      return streamJsonWithHeartbeat(work);
    } catch (error) {
      console.error('Ошибка при парсинге:', error);
      return jsonNoBuffering(
        { error: 'Ошибка при парсинге данных', details: String(error) },
        { status: 500 }
      );
    }
  } catch (error) {
    console.error('Ошибка при обработке запроса:', error);
    return jsonNoBuffering(
      { error: 'Внутренняя ошибка сервера', details: String(error) },
      { status: 500 }
    );
  }
}
