import BaseService from './BaseService.js'; import FalukantPredefineFirstname from '../models/falukant/predefine/firstname.js'; import FalukantPredefineLastname from '../models/falukant/predefine/lastname.js'; import FalukantUser from '../models/falukant/data/user.js'; import User from '../models/community/user.js'; import FalukantCharacter from '../models/falukant/data/character.js'; import RegionData from '../models/falukant/data/region.js'; import RegionType from '../models/falukant/type/region.js'; import { Sequelize } from 'sequelize'; import FalukantStock from '../models/falukant/data/stock.js'; import FalukantStockType from '../models/falukant/type/stock.js'; import { notifyUser } from '../utils/socket.js'; import { differenceInDays } from 'date-fns'; import TitleOfNobility from '../models/falukant/type/title_of_nobility.js'; import Branch from '../models/falukant/data/branch.js'; import BranchType from '../models/falukant/type/branch.js'; import Production from '../models/falukant/data/production.js'; import ProductType from '../models/falukant/type/product.js'; import { Op, fn, col } from 'sequelize'; import Knowledge from '../models/falukant/data/product_knowledge.js'; import Inventory from '../models/falukant/data/inventory.js'; import Stock from '../models/falukant/data/stock.js'; class FalukantService extends BaseService { async getFalukantUserByHashedId(hashedId) { const falukantUser = await FalukantUser.findOne({ include: [{ model: User, as: 'user', attributes: ['username', 'hashedId'], where: { hashedId: hashedId }, } ], }); return falukantUser; } async getUser(hashedUserId) { const falukantUser = await FalukantUser.findOne({ include: [{ model: User, as: 'user', attributes: ['username', 'hashedId'], where: { hashedId: hashedUserId }, }, { model: FalukantCharacter, as: 'character', include: [ { model: FalukantPredefineFirstname, as: 'definedFirstName', attributes: ['name'] }, { model: FalukantPredefineLastname, as: 'definedLastName', attributes: ['name'] }, { model: TitleOfNobility, as: 'nobleTitle', attributes: ['labelTr'] } ], attributes: ['birthdate', 'gender'], }, { model: RegionData, as: 'mainBranchRegion', include: [ { model: RegionType, as: 'regionType', } ], attributes: ['name'], }, { model: Branch, as: 'branches', include: [ { model: BranchType, as: 'branchType', attributes: ['labelTr'] }, { model: RegionData, as: 'region', include: [ { model: RegionType, as: 'regionType', } ], attributes: ['name'], } ] } ], attributes: ['money', 'creditAmount', 'todayCreditTaken'], }); if (!falukantUser) { throw new Error('User not found'); } const character = falukantUser.character; if (character && character.birthdate) { const birthdate = new Date(character.birthdate); birthdate.setHours(0); birthdate.setMinutes(0); const currentDate = new Date(); currentDate.setHours(0); currentDate.setMinutes(0); const ageInDays = differenceInDays(currentDate, birthdate); character.setDataValue('age', ageInDays); } return falukantUser; } async randomFirstName(gender) { const names = await FalukantPredefineFirstname.findAll({ where: { gender: gender } }); return names[Math.floor(Math.random() * names.length)].name; } async randomLastName() { const names = await FalukantPredefineLastname.findAll(); return names[Math.floor(Math.random() * names.length)].name; } async createUser(hashedUserId, gender, firstName, lastName) { try { const user = await this.getUserByHashedId(hashedUserId); const userExistsCheck = await FalukantUser.findOne({ where: { userId: user.id } }); if (userExistsCheck) { throw new Error('User already exists in Falukant.'); } let firstNameObject = await FalukantPredefineFirstname.findOne({ where: { name: firstName } }); let lastNameObject = await FalukantPredefineLastname.findOne({ where: { name: lastName } }); if (!firstNameObject) { firstNameObject = await FalukantPredefineFirstname.create({ name: firstName, gender: gender }); } if (!lastNameObject) { lastNameObject = await FalukantPredefineLastname.create({ name: lastName }); } const randomRegion = await RegionData.findOne({ order: Sequelize.fn('RANDOM'), limit: 1, include: [ { model: RegionType, as: 'regionType', where: { labelTr: 'city' } } ] }); if (!randomRegion) { throw new Error('No region found with the label "city".'); } const titleOfNobility = await TitleOfNobility.findOne({ where: { labelTr: 'noncivil' } }); if (!titleOfNobility) { throw new Error('No title of nobility found with the label "noncivil".'); } const falukantUser = await FalukantUser.create({ userId: user.id, money: 50.00, creditAmount: 0.00, todayCreditTaken: 0.00, creditInterestRate: 0.00, mainBranchRegionId: randomRegion.id, }); const fourteenDaysAgo = new Date(); fourteenDaysAgo.setDate(fourteenDaysAgo.getDate() - 14); const character = await FalukantCharacter.create({ userId: falukantUser.id, regionId: randomRegion.id, firstName: firstNameObject.id, lastName: lastNameObject.id, gender: gender, birthdate: fourteenDaysAgo, titleOfNobility: titleOfNobility.id, }); await FalukantStock.create({ userId: falukantUser.id, regionId: randomRegion.id, stockTypeId: (await FalukantStockType.findOne({ where: [{ label_tr: 'wood' }] })).id, quantity: 10, }); falukantUser['character'] = character; const branchType = await BranchType.findOne({ where: { labelTr: 'fullstack' } }); await Branch.create({ userId: falukantUser.id, regionId: randomRegion.id, branchTypeId: branchType.id, }) notifyUser(user.hashedId, 'reloadmenu', {}); return falukantUser; } catch (error) { console.error('Error creating character'); console.log(error); } } async getInfo(hashedUserId) { try { const user = await User.findOne({ where: { hashedId: hashedUserId } }); if (!user) { throw new Error('User not found'); } const falukantUser = await FalukantUser.findOne({ include: [{ model: User, as: 'user', attributes: ['hashedId'], where: { hashedId: hashedUserId }, }, { model: FalukantCharacter, as: 'character', attributes: ['birthdate', 'health'], }, ], attributes: ['money'] }); if (!falukantUser) { throw new Error('User not found'); } const character = falukantUser.character; if (character && character.birthdate) { const birthdate = new Date(character.birthdate); birthdate.setHours(0); birthdate.setMinutes(0); const currentDate = new Date(); currentDate.setHours(0); currentDate.setMinutes(0); const ageInDays = differenceInDays(currentDate, birthdate); character.setDataValue('age', ageInDays); } return falukantUser; } catch (error) { console.error('Error getting character info'); console.log(error); } } async getBranches(hashedUserId) { try { const falukantUser = await this.getFalukantUserByHashedId(hashedUserId); if (!falukantUser) { throw new Error('User not found'); } const branches = await Branch.findAll({ where: { falukantUserId: falukantUser.id }, include: [ { model: BranchType, as: 'branchType', attributes: ['labelTr'], }, { model: RegionData, as: 'region', attributes: ['name'], } ], attributes: ['id', 'regionId'], order: [['branchTypeId', 'ASC']], }); const enrichedBranches = branches.map(branch => ({ ...branch.toJSON(), isMainBranch: falukantUser.mainBranchRegionId === branch.regionId, })); return enrichedBranches; } catch (error) { console.error('Error in getBranches:', error); throw new Error('Failed to retrieve branches'); } } async getBranch(hashedUserId, branchId) { try { const falukantUser = await this.getFalukantUserByHashedId(hashedUserId); if (!falukantUser) { throw new Error('User not found'); } const branch = await Branch.findOne({ where: { id: branchId, falukantUserId: falukantUser.id }, include: [ { model: BranchType, as: 'branchType', attributes: ['labelTr'], }, { model: RegionData, as: 'region', attributes: ['name'], }, { model: Production, as: 'productions', attributes: ['quantity', 'startTimestamp'], include: [ { model: ProductType, as: 'productType', attributes: ['id', 'category', 'labelTr', 'sellCost', 'productionTime'], } ] } ], attributes: ['id', 'regionId'], }); if (!branch) { throw new Error('Branch not found'); } return branch; } catch (error) { console.error('Error in getBranch:', error); throw new Error('Failed to retrieve branch'); } } async getStock(hashedUserId, branchId) { try { const falukantUser = await this.getFalukantUserByHashedId(hashedUserId); if (!falukantUser) { throw new Error('User not found'); } const branch = await Branch.findOne({ where: { id: branchId, falukantUserId: falukantUser.id } }); if (!branch) { throw new Error('Branch not found'); } const stock = await FalukantStock.findOne({ where: { regionId: branch.regionId, userId: falukantUser.id } }); return stock; } catch (error) { console.error('Error in getStock:', error); throw new Error('Failed to retrieve stock'); } } async createStock(hashedUserId, branchId, stockData) { try { const falukantUser = await this.getFalukantUserByHashedId(hashedUserId); if (!falukantUser) { throw new Error('User not found'); } const branch = await Branch.findOne({ where: { id: branchId, falukantUserId: falukantUser.id } }); if (!branch) { throw new Error('Branch not found'); } const stock = await FalukantStock.create({ userId: falukantUser.id, regionId: branch.regionId, stockTypeId: stockData.stockTypeId, quantity: stockData.quantity, }); return stock; } catch (error) { console.error('Error in createStock:', error); throw new Error('Failed to create stock'); } } async createProduction(hashedUserId, branchId, productId, quantity) { const falukantUser = await this.getFalukantUserByHashedId(hashedUserId); if (!falukantUser) { throw new Error('User not found'); } const branch = await Branch.findOne({ where: { id: branchId, falukantUserId: falukantUser.id } }); if (!branch) { throw new Error('Branch not found'); } const product = await ProductType.findOne({ where: { id: productId } }); if (falukantUser.money < quantity * product.category * 7) { throw new Error('notenoughmoney'); } const production = await Production.create({ branchId: branch.id, productId: productId, quantity: quantity, }); falukantUser.update({ money: falukantUser.money - quantity * product.category * 7 }); notifyUser(falukantUser.user.hashedId, 'falukantUpdateStatus', {}); notifyUser(falukantUser.user.hashedId, 'falukantBranchUpdate', { branchId: branch.id }); return production; } async getProduction(hashedUserId, branchId) { try { const falukantUser = await this.getFalukantUserByHashedId(hashedUserId); if (!falukantUser) { throw new Error('User not found'); } const branch = await Branch.findOne({ where: { id: branchId, falukantUserId: falukantUser.id } }); if (!branch) { throw new Error('Branch not found'); } const production = await FalukantProduction.findOne({ where: { regionId: branch.regionId } }); return production; } catch (error) { console.error('Error in getProduction:', error); throw new Error('Failed to retrieve production'); } } async getProducts(hashedUserId) { try { const falukantUser = await this.getFalukantUserByHashedId(hashedUserId); if (!falukantUser) { throw new Error('User not found'); } const products = await ProductType.findAll({ where: { category: { [Op.lte]: falukantUser.certificate } }, include: [ { model: Knowledge, as: 'knowledges', attributes: ['knowledge'], } ], attributes: ['labelTr', 'id', 'sellCost', 'productionTime', 'category'], }); return products; } catch (error) { console.error('Error in getProducts:', error); throw new Error('Failed to retrieve products'); } } async getInventory(hashedUserId, branchId) { try { const falukantUser = await this.getFalukantUserByHashedId(hashedUserId); if (!falukantUser) { throw new Error('User not found'); } const branchFilter = branchId ? { id: branchId, falukantUserId: falukantUser.id } : { falukantUserId: falukantUser.id }; const branches = await Branch.findAll({ where: branchFilter, include: [ { model: FalukantStock, as: 'stocks', include: [ { model: FalukantStockType, as: 'stockType', }, ], }, { model: RegionData, as: 'region', include: [ { model: RegionType, as: 'regionType', }, ], }, ], }); const stockIds = branches.flatMap(branch => branch.stocks.map(stock => stock.id)); const inventoryItems = await Inventory.findAll({ where: { stockId: stockIds }, include: [ { model: FalukantStock, as: 'stock', include: [ { model: Branch, as: 'branch', include: [ { model: RegionData, as: 'region', include: [ { model: RegionType, as: 'regionType', }, ], }, ], }, { model: FalukantStockType, as: 'stockType', }, ], }, { model: ProductType, as: 'productType', }, ], }); const groupedInventory = inventoryItems.reduce((acc, item) => { const region = item.stock.branch.region; const key = `${region.id}-${item.productType.id}-${item.quality}`; if (!acc[key]) { acc[key] = { region, product: item.productType, quality: item.quality, totalQuantity: 0, }; } acc[key].totalQuantity += item.quantity; return acc; }, {}); const sortedInventory = Object.values(groupedInventory).sort((a, b) => { if (a.region.id !== b.region.id) { return a.region.id - b.region.id; } if (a.product.id !== b.product.id) { return a.product.id - b.product.id; } return a.quality - b.quality; }); return sortedInventory; } catch (error) { console.error('Error in getInventory:', error); throw new Error('Failed to retrieve inventory'); } } async sellProduct(hashedUserId, branchId, productId, quality, quantity) { try { const falukantUser = await this.getFalukantUserByHashedId(hashedUserId); if (!falukantUser) { throw new Error('User not found'); } const branch = await Branch.findOne({ where: { id: branchId, falukantUserId: falukantUser.id } }); if (!branch) { throw new Error('Branch not found'); } const product = await ProductType.findOne({ where: { id: productId } }); if (!product) { throw new Error('Product not found'); } const stock = await Stock.findOne({ where: { branchId: branch.id, } }); if (!stock) { throw new Error('Stock not found'); } if (stock.quantity < quantity) { throw new Error('Not enough stock'); } await FalukantStock.decrement('quantity', { by: quantity, where: { regionId: branch.regionId, stockTypeId: product.id } }); const inventory = await Inventory.create({ stockId: stock.id, quality, quantity }); return inventory; } catch (error) { console.error('Error in sellProduct:', error); throw new Error('Failed to sell product'); } } } export default new FalukantService();